source("../Rscripts/BaseScripts.R")
library(data.table)
library(DataCombine)
library(gridExtra)
library(scales)
library(corrplot)
library(gplots)
pops.info<-read.csv("../Data/Sample_metadata_892pops.csv")
pops<-unique(pops.info$Population.Year)
pops2<-pops[grep("17", pops)]

1 Prepare SNPs for BayEnv

1.1 Prune the VCF file with the strict standard and filter the loci

#at farm (prune_vcf_froBayEnv.sh)
module load plink
#add variant ID names for pruning 
plink --vcf /home/ktist/ph/data/new_vcf/MD7000/PH_DP600_7000_minQ20_minMQ30_NS0.5_maf05.vcf.gz --set-missing-var-ids @:#[ph] --make-bed --out /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/MD7000_maf0.05 

plink --bfile /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/MD7000_maf0.05  --recode --tab --out /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/MD7000_maf0.05 

plink --file  /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/MD7000_maf0.05  --indep-pairwise 1000kb 1 0.15 --out /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/prune_strict_1000k_0.15

#Different parameters (window size and R2 number) did not change the number of pruned loci drastically:
# indep-pairwise 50 5 0.5 (original parameters) = 270000 loci
# stricter parameters 1000k 1 0.15   = 205726

# Filter the vcf file (prune_convert_BAYENV.sh)
# First, reformat the prune.in  file as a 'CHR' 'POS' tab-delimited txt 
sed -e s/\\[ph\\]//g prune_strict_1000k_0.15.prune.in | awk  '{gsub(":","\t",$0); print;}' > prune_strict_1000k_0.15.in.txt

# Remove the pruned loci
bcftools view -R /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/prune_strict_1000k_0.15.in.txt /home/ktist/ph/data/new_vcf/MD7000/PH_DP600_7000_minQ20_minMQ30_NS0.5_maf05.vcf.gz > /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/pruned_strict_PH_all_maf05.vcf

bgzip /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/pruned_strict_PH_all_maf05.vcf


# Subset the each file (subset_prunedVCF2.sh)
bcftools view -Oz -S /home/ktist/ph/data/new_vcf/population/BC17.txt --threads 24  /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/pruned_strict_PH_all_maf05.vcf.gz > /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_BC17_maf05.vcf.gz
bcftools index /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_BC17_maf05.vcf.gz 
bcftools view -Oz -S /home/ktist/ph/data/new_vcf/population/CA17.txt --threads 24  /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/pruned_strict_PH_all_maf05.vcf.gz > /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_CA17_maf05.vcf.gz
bcftools index /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_CA17_maf05.vcf.gz 
bcftools view -Oz -S /home/ktist/ph/data/new_vcf/population/PWS07.txt --threads 24  /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/pruned_strict_PH_all_maf05.vcf.gz > /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_PWS07_maf05.vcf.gz
bcftools index /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_PWS07_maf05.vcf.gz 
bcftools view -Oz -S /home/ktist/ph/data/new_vcf/population/PWS17.txt --threads 24  /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/pruned_strict_PH_all_maf05.vcf.gz > /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_PWS17_maf05.vcf.gz
bcftools index /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_PWS17_maf05.vcf.gz 
bcftools view -Oz -S /home/ktist/ph/data/new_vcf/population/PWS91.txt --threads 24  /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/pruned_strict_PH_all_maf05.vcf.gz > /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_PWS91_maf05.vcf.gz
bcftools index /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_PWS91_maf05.vcf.gz 
bcftools view -Oz -S /home/ktist/ph/data/new_vcf/population/PWS96.txt --threads 24  /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/pruned_strict_PH_all_maf05.vcf.gz > /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_PWS96_maf05.vcf.gz
bcftools index /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_PWS96_maf05.vcf.gz 
bcftools view -Oz -S /home/ktist/ph/data/new_vcf/population/SS06.txt --threads 24  /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/pruned_strict_PH_all_maf05.vcf.gz > /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_SS06_maf05.vcf.gz
bcftools index /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_SS06_maf05.vcf.gz 
bcftools view -Oz -S /home/ktist/ph/data/new_vcf/population/SS17.txt --threads 24  /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/pruned_strict_PH_all_maf05.vcf.gz > /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_SS17_maf05.vcf.gz
bcftools index /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_SS17_maf05.vcf.gz 
bcftools view -Oz -S /home/ktist/ph/data/new_vcf/population/SS96.txt --threads 24  /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/pruned_strict_PH_all_maf05.vcf.gz > /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_SS96_maf05.vcf.gz
bcftools index /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_SS96_maf05.vcf.gz 
bcftools view -Oz -S /home/ktist/ph/data/new_vcf/population/TB06.txt --threads 24  /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/pruned_strict_PH_all_maf05.vcf.gz > /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_TB06_maf05.vcf.gz
bcftools index /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_TB06_maf05.vcf.gz 
bcftools view -Oz -S /home/ktist/ph/data/new_vcf/population/TB17.txt --threads 24  /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/pruned_strict_PH_all_maf05.vcf.gz > /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_TB17_maf05.vcf.gz
bcftools index /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_TB17_maf05.vcf.gz 
bcftools view -Oz -S /home/ktist/ph/data/new_vcf/population/TB91.txt --threads 24  /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/pruned_strict_PH_all_maf05.vcf.gz > /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_TB91_maf05.vcf.gz
bcftools index /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_TB91_maf05.vcf.gz 
bcftools view -Oz -S /home/ktist/ph/data/new_vcf/population/TB96.txt --threads 24  /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/pruned_strict_PH_all_maf05.vcf.gz > /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_TB96_maf05.vcf.gz
bcftools index /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_TB96_maf05.vcf.gz 
bcftools view -Oz -S /home/ktist/ph/data/new_vcf/population/WA17.txt --threads 24  /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/pruned_strict_PH_all_maf05.vcf.gz > /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_WA17_maf05.vcf.gz
bcftools index /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_WA17_maf05.vcf.gz 

#Count the alleles (extract_AC.sh)
bcftools query -f '%CHROM  %POS  %INFO/AC  %INFO/AN\n' /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_PWS17_maf05.vcf.gz > /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/pruned_AC_PWS17.txt 
bcftools query -f '%CHROM  %POS  %INFO/AC  %INFO/AN\n' /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_PWS91_maf05.vcf.gz > /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/pruned_AC_PWS91.txt 
bcftools query -f '%CHROM  %POS  %INFO/AC  %INFO/AN\n' /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/Pruned_PWS96_maf05.vcf.gz > /home/ktist/ph/data/new_vcf/MD7000/plinkfiles/pruned_AC_PWS96.txt 

1.2 Create Allele Count file

  • Used the outputs from extract_AC.sh to get the allele counts for each population at each loci
pops.info<-read.csv("../Data/Sample_metadata_892pops.csv")
pops<-unique(pops.info$Population.Year)

#Pruned position info
prun<-read.table("../Data/new_vcf/AC/Pruned_positions.txt")
prun$V1<-paste0("chr",prun$V1)
colnames(prun)<-c("chr","pos")

snpfile<-data.frame(n=1:(2*nrow(prun)))
for (i in 1:length(pops)){
    df<-read.table(paste0("../Data/new_vcf/AC/Pruned_AC_",pops[i],".txt"))
    colnames(df)<-c("chr","pos","AC","AN")
    #majorAC data frame
    maj<-transform(df, AC=AN-AC)
    newdf<-rbind(maj[,1:3], df[,1:3])
    #reorder the rows
    n<-nrow(df)
    newdf2<-newdf[kronecker(1:n, c(0, n), "+"), ]
    newdf2<-newdf2[order(newdf2$chr,newdf2$pos),]
    colnames(newdf2)[3]<-pops[i]
    #combine the data
    if (i==1) snpfile<-cbind(snpfile,newdf2)
    if (i!=1) snpfile<-cbind(snpfile, newdf2[,3])
}

colnames(snpfile)[5:17]<-pops[2:14]
#save the snpfi\e
snps<-snpfile[,-1]

#filter out the low allele count loci (1-10)
remove<-which(rowSums(snps[,3:16])<=10) #2143 loci
even<-remove[sapply(remove, function(x) x%%2==0)] #all odd numbers
remove<-unname(remove)
remove<-c(remove, remove+1)
remove<-remove[order(remove)]
snps2<-snps[-remove,]

write.csv(snps2, "../Output/BayEnv/Pruned_allPops_snpsfile.csv")
write.table(snps2[,3:16], "../Output/BayEnv/Pruned_allPops_snpsfile.txt", sep="\t", row.names = F,col.names = F, quote=F)

1.3 Filter loci with low representation as 200k snps are too many

# Filter more loci since 200k is too many.
#snps2<-read.csv("../Output/BayEnv/Pruned_allPops_snpsfile.csv", row.names = 1)


# This function filters a snp file based on the total allele count (of minor alleles)
#input df =[,c("chr,"pos", pop1,pop2,...)]
trimsnp<-function(x, df){
    remove<-which(rowSums(df[,3:ncol(df)])<=x) 
    remove_even<-remove[sapply(remove, function(x) x%%2==0)]
    remove_odd<-remove[!(remove %in% remove_even)]
    remv<-c(remove_odd, remove_odd+1, remove_even, remove_even-1)
    snpsNew<-df[-remv,]
    return(snpsNew)
}

snps_25<-trimsnp(492, snps2) #13647/2 loci 
snps_20<-trimsnp(178, snps2) #99536/2 loci

#write.table(snps_25[,3:16], "../Output/BayEnv/Pruned_allPops_25Percent_snpsfile.txt", sep="\t", row.names = F,col.names = F, quote=F)
#write.table(snps_20[,3:16], "../Output/BayEnv/Pruned_allPops_20Percent_snpsfile.txt", sep="\t", row.names = F,col.names = F, quote=F)



#Create year 2017 only file
#snps2<-read.csv("../Output/BayEnv/Pruned_allPops_snpsfile.csv", row.names = 1)
snps17<-snps2[,c(1,2,grep("17",colnames(snps2)))]


# Create 2017 file with chromosome and pop based filtering
pops2<-pops[grep("17", pops)]
# 1. create an allele depth table 
snpfile<-data.frame(n=1:nrow(prun))
for (i in 1:length(pops2)){
    df<-read.table(paste0("../Data/new_vcf/AC/Pruned_AC_",pops2[i],".txt"))
    colnames(df)<-c("chr","pos","AC",pops2[i])
    if (i==1) snpfile<-cbind(snpfile,df[,c(1,2,4)])
    if (i!=1) {
        snpfile<-cbind(snpfile, df[,4])
        colnames(snpfile)[(i+3)]<-pops2[i]}
}

#pop size
popsize<-read.csv("../Data/Samplesize.per.pop.csv")

# Set the minimum cutoff for allele count total per population (70%)
popsize$cutoff<-as.integer(popsize$Freq*2*.7)
popn<-popsize$cutoff[popsize$Var1 %in%pops2]

# substract the cutoff allele count
snpfile3<-snpfile[,4:9]-popn
snpfile3<-cbind(snpfile[,2:3], snpfile3)
# this contains if the loci in a pop are above (postive) or below (negative) the cutoff allele depth 

has_negatives<-apply(snpfile3[,3:8], 1, function(x) any(x<0))
length(has_negatives[has_negatives==F])
#18908 loci has >70% of individuals represented in each pop

#filtered to 18908 loci
positives<-which(has_negatives==F)
filtered<-snpfile3[positives,]

table(filtered$chr)
# chr1 chr10 chr11 chr12 chr13 chr14 chr15 chr16 chr17 chr18 chr19  chr2 chr20 
#  871   572   617   645   571   591   982   748   590  1518   581  1468   431 
#chr21 chr22 chr23 chr25 chr26  chr3  chr4  chr5  chr6  chr7  chr8  chr9 
#  471   573   849   710   837   624   719   481   832  1145   561   921 
 #chr 2 has the highest number of loci

filtered_snpfile<-data.frame(n=1:(2*nrow(filtered)))
for (i in 1:length(pops2)){
    df<-read.table(paste0("../Data/new_vcf/AC/Pruned_AC_",pops2[i],".txt"))
    colnames(df)<-c("chr","pos","AC","AN")
    df<-merge(filtered[,1:2], df, by=c("chr","pos"))
    #majorAC data frame
    maj<-transform(df, AC=AN-AC)
    newdf<-rbind(maj[,1:3], df[,1:3])
    #reorder the rows
    n<-nrow(df)
    newdf2<-newdf[kronecker(1:n, c(0, n), "+"), ]
    newdf2<-newdf2[order(newdf2$chr,newdf2$pos),]
    #combine the data
    if (i==1) filtered_snpfile<-cbind(filtered_snpfile,newdf2)
    if (i!=1) filtered_snpfile<-cbind(filtered_snpfile, newdf2[,3])
    colnames(filtered_snpfile)[i+3]<-pops2[i]
}

filtered_snpfile<-filtered_snpfile[,-1]

new<-trimsnp(5, filtered_snpfile) #18705 loci

write.csv(new, "../Output/BayEnv/Y2017_filtered_snpsfile.csv")

write.table(new[,3:8], "../Output/BayEnv/Y2017_filtered_snpsfile_18705.txt", sep="\t", row.names = F,col.names = F, quote=F)


#change the filtering proportion (% of pop with a minor allele) and see how many snps are retained
filterSnpCount<-function(x){
    popsize$cutoff<-as.integer(popsize$Freq*2*x)
    popn<-popsize$cutoff[popsize$Var1 %in%pops2]
    snpfile3<-snpfile[,4:9]-popn
    snpfile3<-cbind(snpfile[,2:3], snpfile3)
    has_negatives<-apply(snpfile3[,3:8], 1, function(x) any(x<0))
    print(length(has_negatives[has_negatives==F]))
}

# x is the proportion of populations that have a particular snp
filterSnp<-function(x){
    popsize$cutoff<-as.integer(popsize$Freq*2*x)
    popn<-popsize$cutoff[popsize$Var1 %in%pops2]
    snpfile3<-snpfile[,4:9]-popn
    snpfile3<-cbind(snpfile[,2:3], snpfile3)
    has_negatives<-apply(snpfile3[,3:8], 1, function(x) any(x<0))
    print(length(has_negatives[has_negatives==F]))
    positives<-which(has_negatives==F)
    filtered<-snpfile3[positives,]
    
    filtered_snpfile<-data.frame(n=1:(2*nrow(filtered)))
    for (i in 1:length(pops2)){
        df<-read.table(paste0("../Data/new_vcf/AC/Pruned_AC_",pops2[i],".txt"))
        colnames(df)<-c("chr","pos","AC","AN")
        df<-merge(filtered[,1:2], df, by=c("chr","pos"))
        #majorAC data frame
        maj<-transform(df, AC=AN-AC)
        newdf<-rbind(maj[,1:3], df[,1:3])
        #reorder the rows
        n<-nrow(df)
        newdf2<-newdf[kronecker(1:n, c(0, n), "+"), ]
        newdf2<-newdf2[order(newdf2$chr,newdf2$pos),]
        #combine the data
        if (i==1) filtered_snpfile<-cbind(filtered_snpfile,newdf2)
        if (i!=1) filtered_snpfile<-cbind(filtered_snpfile, newdf2[,3])
        colnames(filtered_snpfile)[i+3]<-pops2[i]
        }
    return(filtered_snpfile)
}



newsnps<-filterSnp(.75)  #8707 loci
newsnps<-newsnps[,-1]
# re-filter to remove sites that have <5 allele count

new2<-trimsnp(5, newsnps) #8621 loci

write.table(new2[,3:8], "../Output/BayEnv/Y2017_filtered_snpsfile_8621.txt", sep="\t", row.names = F,col.names = F, quote=F)
write.csv(new2,"../Output/BayEnv/Y2017_filtered_snpsfile_8621.csv", row.names = F)

2 Create envfile

2.1 Standardize the water temperature file

water<-read.csv("../Output/BayEnv/watertemp.csv")
water$C<-(water$mean-32)*5/9
mean<-mean(water$C)
sd<-sd(water$C)
water$C_std<-(water$C-mean)/sd
water2<-data.frame(t(water[,c("pop","C_std")]))
colnames(water2)<-water2[1,]
water2<-water2[,c("BC","CA","PWS","SS","TB","WA")]
write.table(water2[2,], "../Output/BayEnv/temp_std.txt", sep = "\t",quote = F, row.names = F, col.names = F)

# Prepare min/mean/max files created from 2017 data 

env<-read.csv("../Data/env_data/sst_2017.csv")
# BC= Hecate Strait, BC2 = South Moresby (use BC2 for now)
env<-env[env$Pop!="BC",]
env$Pop[env$Pop=="BC2"]<-"BC"
# 1. mean
mean<-mean(env$Mean)
sd<-sd(env$Mean)
env$mean_std<-(env$Mean-mean)/sd

env1<-data.frame(t(env[,c("Pop","mean_std")]))
colnames(env1)<-env1[1,]
env1<-env1[,c("BC","CA","PWS","SS","TB","WA")]
write.table(env[2,], "../Output/BayEnv/sst2017_mean_std.txt", sep = "\t",quote = F, row.names = F, col.names = F)

#2. min
mean<-mean(env$Min)
sd<-sd(env$Min)
env$min_std<-(env$Min-mean)/sd
env1<-data.frame(t(env[,c("Pop","min_std")]))
colnames(env1)<-env1[1,]
env1<-env1[,c("BC","CA","PWS","SS","TB","WA")]
write.table(env[2,], "../Output/BayEnv/sst2017_min_std.txt", sep = "\t",quote = F, row.names = F, col.names = F)

#Max
mean<-mean(env$Max)
sd<-sd(env$Max)
env$max_std<-(env$Max-mean)/sd

env1<-data.frame(t(env[,c("Pop","max_std")]))
colnames(env1)<-env1[1,]
env1<-env1[,c("BC","CA","PWS","SS","TB","WA")]
write.table(env[2,], "../Output/BayEnv/sst2017_max_std.txt", sep = "\t",quote = F, row.names = F, col.names = F)

2.2 Add latitude informaiton

#add latitude?
lat<-c(52.1,37.69,60.54, 59.93, 58.8, 47.6)
#standardize
lat2<-(lat-mean(lat))/sd(lat)
water2[3,]<-lat2
write.table(water2[2:3,], "../Output/BayEnv/temp_lat_std.txt", sep = "\t",quote = F, row.names = F, col.names = F)

t<-unlist(as.vector(water2[2,]))
t<-as.numeric(t)
cor.test(lat, t, method="pearson")
#t = -1.9363, df = 4, p-value = 0.1249
#latitude and temperature do not show correlation

3 Run BayEnv2 on Farm to obtain a Covariacne Matrix

3.1 Create slurm scripts to estiamte the covariacne matrix

3.1.1 “Run as ‘Pooled NGS data’

sink(paste0("../Data/Slurmscripts/bayenv_run.sh"))
cat("#!/bin/bash -l\n")
cat(paste0("#SBATCH --job-name=bayenv \n"))
cat(paste0("#SBATCH --mem=16G \n")) 
cat(paste0("#SBATCH --ntasks=8 \n")) 
cat(paste0("#SBATCH -e =bayenv.err  \n"))
cat(paste0("#SBATCH --time=72:00:00  \n"))
cat(paste0("#SBATCH -p high  \n"))
cat("\n\n")
cat("module load bayenv2 \n\n") 
  
cat("bayenv2 -i /ktist/ph/data/bayenv/Y2017_filtered_snpsfile_8621.txt -s samplesize.txt -p 6 -k 100000 -r 12345 -x  > Y2017_matrix3.out \n")
sink(NULL)

#extract the last covariance matrix as Y2017matrix.txt

3.1.2 Visualize the correlation matrix from BayEnv2

  • Run with ‘Pooled NGS’ method
cov = as.matrix(read.table(file="../Output/BayEnv/Y2017_matrix.txt",header=F))
mat = cov2cor(cov)

row.names(mat)<-pops2
colnames(mat)<-pops2
{pdf("../Output/BayEnv/Corplot_y2017_8621snps_run1.pdf", width = 5, height=5)
corrplot(mat, is.corr=T)
dev.off()}

mat<-mat[,c("TB17","PWS17","SS17","BC17","WA17","CA17")]
mat<-mat[c("TB17","PWS17","SS17","BC17","WA17","CA17"),]

mat2<-mat
diag(mat2)<-NA
mat2[upper.tri(mat2)]<-NA
mat2m<-melt(mat2)

mat2m$Var1<-factor(mat2m$Var1, levels=c("TB17","PWS17","SS17","BC17","WA17","CA17"))
mat2m$Var2<-factor(mat2m$Var2, levels=c("TB17","PWS17","SS17","BC17","WA17","CA17"))

mat2m$color<-"a"
mat2m$color[mat2m$value>0.01]<-"b"
ggplot(mat2m, aes(x=Var1, y=Var2, fill=value))+
    geom_tile(color = "white")+
    scale_fill_gradientn(colors=c("#073466","white"), limits=c(min(mat2m$value, na.rm=T), 0.04), na.value="gray90", 
                         name="Correlation")+
    theme_minimal()+ xlab("")+ylab("")+
    geom_text(aes(Var1, Var2, label = round(value, digits = 3), color=color),  size = 5)+
    scale_color_manual(values=c( "white","black"), guide='none')

ggsave("../Output/BayEnv/Correlation_plot_8621snps.png", width = 6, height = 4.5, dpi=300)    

palette1 <- colorRampPalette(c("blue","white","red"))(n = 299)

mat3<-mat
diag(mat3)<-NA
{pdf("../Output/BayEnv/Heatmap_y2017_8621snps_run1.pdf", width =5, height=3)
heatmap.2(mat3,col=palette1,symm=T,symkey=T,dendrogram="row",trace="none",Colv="Rowv",,breaks = seq(-0.1, 0.1, length.out = 300),labCol="" ,key=F,cexRow=1)
dev.off()}

#Correlations are different from Fst.

Fst plot

  • Correlations are slightly different from the relationships from the Fst values.



4 Run BayEnv2 with the matrix produced in the step1 with the temperature data

# Run BayEnv2 for all SNPS with environmental data
sink(paste0("../Data/Slurmscripts/bayenv_run2.sh"))
cat("#!/bin/bash -l\n")
cat(paste0("#SBATCH --job-name=bayenv2 \n"))
cat(paste0("#SBATCH --mem=16G \n")) 
cat(paste0("#SBATCH --ntasks=8 \n")) 
cat(paste0("#SBATCH -e =bayenv2.err  \n"))
cat(paste0("#SBATCH --time=72:00:00  \n"))
cat(paste0("#SBATCH -p high  \n"))
cat("\n\n")
cat("module load bayenv2 \n\n") 

#First split the SNPfiles based on calc_bfs.sh 
cat("split -a 10 -l 2 Y2017_filtered_snpsfile_8621.txt snp_batch \n\n")

#Run bayenv2 for each snp
cat("for f in $(ls snp_batch*\n")
cat("do\n")
cat("bayenv2 -i $f -e temp_std.txt -m Y2017_matrix.txt -k 100000 -r 12345 -p 6 -n 1 -t -c \n")
cat("done \n")
sink(NULL)

4.1 Results from BayEnv2 Run1

bf<-read.table("../Output/BayEnv/RUN1_bf_environ.temp_std.txt", header = F)
colnames(bf)<-c("batch_id", "BF","rho","r")

# List all split file names:
# ls snp_batch* >snp_filenames.txt

names<-read.table("../Output/BayEnv/snp_filenames.txt")

#snp names
snps<-read.csv("../Output/BayEnv/Y2017_filtered_snpsfile_8621.csv")
snps_name<-snps[seq(1,nrow(snps),by=2),c("chr","pos")]
snps_name$batch_id<-names$V1

# now merge the two tables
bf<-merge(snps_name, bf, by="batch_id")
bf$chrom<-as.integer(gsub("chr",'',bf$chr))
bf<-bf[order(bf$chrom, bf$pos),]
bf$n<-1:nrow(bf)
evens<-paste0("chr",seq(2,26, by=2))
bf$color<-"a"
bf$color[bf$chr %in% evens]<-"b"
ggplot(bf, aes(x=n,y=BF, color=color))+xlab('')+
    geom_point(size=0.5, alpha=0.6)+ylab("Bayes facator")+
    theme_classic()+
    scale_color_manual(values=c("gray70","steelblue"), guide="none")+
    theme(axis.text.x = element_blank())
ggsave("../Output/baysFactor_acrossGenome_Y2017_run1.png", width = 7, height = 3, dpi=300)

  • Remove the 2 outliers & plot in log scale
#remove the 2 outliers
ggplot(bf, aes(x=n,y=BF, color=color))+xlab('')+
    geom_point(size=0.5, alpha=0.6)+ylab("Bayes facator")+
    theme_classic()+
    scale_color_manual(values=c("gray70","steelblue"), guide="none")+
    theme(axis.text.x = element_blank(), axis.ticks.x=element_blank())+
    scale_y_continuous(labels = label_comma(), trans = 'log10', limits=c(0.05,10000))
ggsave("../Output/baysFactor_acrossGenome_Y2017_run1_logscaled.png", width = 7, height = 3, dpi=300)

#rho values
ggplot(bf, aes(x=n,y=rho, color=color))+xlab('')+
    geom_point(size=0.5, alpha=0.6)+ylab("Speaerman's rho")+
    theme_classic()+
    scale_color_manual(values=c("gray70","steelblue"), guide="none")+
    theme(axis.text.x = element_blank(), axis.ticks.x=element_blank())
ggsave("../Output/Rho_acrossGenome_Y2017_run1.png", width = 7, height = 3, dpi=300)

4.2 Spearman’s rho values

4.3 Find highly ranked snps in BF and ρ

#top 1% of BF
bf<-bf[order(bf$BF, decreasing = T),]
n<-as.integer(nrow(bf)*0.01)
top1bf<-bf[1:n, ]

min(top1bf$BF)
#54.94 is the lowest = cutoff values

hist(bf$BF[bf$BF<100])
#proportion of BF>20
length(bf$BF[bf$BF>=20])/nrow(bf)
#0.01600742   1.6% of BFs are over 20

#top 1% of rho
bf<-bf[order(abs(bf$rho), decreasing = T),]
#n<-as.integer(nrow(bf)*0.01)
top1rho<-bf[1:n, ]

min(abs(top1rho$rho))
#0.53083  rho value cutoff = 0.53083

hist(abs(bf$rho))
# Manual says x (top x% BF) < y (top y% rho) 

#increase the cutoff value to 60?
length(bf$BF[bf$BF>=60])/nrow(bf) 
# 0.963%
top1bf<-top1bf[top1bf$BF>=60,]

outliers<-intersect(top1bf$batch_id, top1rho$batch_id)
#29 candidate loci 

outs<-bf[bf$batch_id %in% outliers,]

# plot across the genome to see the locations of outliers
outs<-outs[order(outs$n),]

#no chromosome 24
chroms<-c(1:23, 25:26)
poss<-data.frame(chr=paste0("chr",chroms))
k=1
i=1
for (j in chroms){
        df<-bf[bf$chr==paste0("chr",j),]
        poss$start[i]<-k
        poss$end[i]<-k+nrow(df)-1
        k=k+nrow(df)
        i=i+1
}
poss$x<-poss$start+(poss$end-poss$start)/2

bf$outlier<-"n"
bf$outlier[bf$color=="b"]<-"o"
bf$outlier[bf$batch_id %in%outliers]<-"y"

ggplot(bf, aes(x=n, y=BF, color=outlier))+
    geom_point(size=0.6)+
    scale_y_continuous(trans='log10',label=label_comma())+
    scale_color_manual(values=c("#C0C0C088","#ADD8E680","#FF3293B3"), guide="none")+
    scale_x_continuous(name="Chromosome", breaks=poss$x, labels=chroms)+
    theme_classic()+
    geom_hline(yintercept = 60, color="darkred", size=0.3, linetype=2)

ggsave("../Output/BayEnv/Run1_BF_rho_combined_outliers_8621snps.png", width = 7, height=3.5, dpi=300)

#find annotations for the outlier loci

chrs<-unique(outs$chr)
genes<-data.frame()
annotations<-list()
k=1
t=1
for (i in 1:length(chrs)){
    df<-outs[outs$chr==chrs[i],]
    ch<-as.integer(gsub("chr",'',chrs[i]))
    anno<-read.table(paste0("../Data/annotations/annotation_byChromosome/", ch), sep="\t")
    colnames(anno)[4:5]<-c("start","end")
    
    for (j in 1:nrow(df)){
        annot<-anno[anno$start<=df$pos[j] & anno$end>df$pos[j],]
        if(nrow(annot)!=0){
             gene<-annot[annot$V3=="gene",]
            if (nrow(gene)>0) genes<-rbind(genes, gene); k=k+1
            if (nrow(gene)==0) annotations[[t]]<-annot; t=t+1
        }
    }
}


gene_list<-genes[,1:5]

for (i in 1: nrow(genes)){
    info<-unlist(strsplit(genes$V9[i], "\\;"))
    info<-str_trim(info, side="left")
    id<-info[grep("gene_id", info)]
    gene_list$gene_id[i]<-gsub("gene_id ",'', id)
    
    name<-info[grep("gene_name", info)]
    if (length(name)!=0) gene_list$gene_name[i]<-gsub("gene_name ",'', name)
    if (length(name)==0) gene_list$gene_name[i]<-''
    
    type<-info[grep("gene_biotype", info)]
    gene_list$gene_type[i]<-gsub("gene_biotype ",'', type)
}     

write.csv(gene_list, "../Output/BayEnv/Run1_Outliers_gene_info.csv")
sink("../Output/BayEnv/genes.txt")
cat(paste(gene_list$gene_id),sep=";")
sink(NULL)
# enter the genes to ShinyGo

# Also look at the regions surrounding the loci (this is more sensible approach as all linked loci are removed)

#Create a bed file containing regions near outlier loci

out_bed1<-outs[,c("chr","pos")]
out_bed1$start<-outs$pos-200000
out_bed1$end<-outs$pos+200000
write.table(out_bed1[,c(1,3,4)], paste0("../Output/BayEnv/Run1_outlier_bed_200kbuffer.bed"),quote = F, row.names = F, col.names = T,sep = "\t")

out_bed2<-outs[,c("chr","pos")]
out_bed2$start<-outs$pos-100000
out_bed2$end<-outs$pos+100000
write.table(out_bed2[,c(1,3,4)], paste0("../Output/BayEnv/Run1_outlier_bed_100kbuffer.bed"),quote = F, row.names = F, col.names = T,sep = "\t")

#create a new vcf with the bed files 
bedfiles<-list.files("../Output/BayEnv", pattern=".bed")
sink("../Output/BayEnv/ceateVCFs_bed1.sh")
cat("#!/bin/bash \n\n")
for (i in 1:length(bedfiles)){
    fname<-gsub(".bed",'', bedfiles[i])
    cat(paste0("vcftools --gzvcf Data/new_vcf/PH_DP600_7000_minQ20_minMQ30_NS0.5_maf05.vcf.gz --bed Output/BayEnv/", bedfiles[i], " --out Output/BayEnv/", fname," --recode --keep-INFO-all \n"))
}
sink(NULL) 

#create a bash script to run snpEff
vfiles<-list.files("../Output/BayEnv/", pattern=".recode.vcf")

sink("~/programs/snpEff/runsnpEff_bayenv_outliers1.sh")
cat("#!/bin/bash \n\n")
for (i in 1:length(vfiles)){
    fname<-gsub(".recode.vcf","",vfiles[i])
    cat(paste0("java -Xmx8g -jar snpEff.jar Ch_v2.0.2.99 ~/Projects/PacHerring/Output/BayEnv/",vfiles[i], " -stats ~/Projects/PacHerring/Output/BayEnv/Outlier_genes/",fname,".html >  ~/Projects/PacHerring/Output/BayEnv/Outlier_genes/Anno.",fname,".vcf \n"))
    
    #extract the annotation information
    cat(paste0("bcftools query -f '%CHROM %POS %INFO/AF %INFO/ANN\\n' ~/Projects/PacHerring/Output/BayEnv/Outlier_genes/Anno.",fname,".vcf > ~/Projects/PacHerring/Output/BayEnv/Outlier_genes/",fname,"_annotation \n\n"))

}
sink(NULL)  

5 A covariacne matrix with 18705 SNPs

  • Run with “Pooled NGS” method
cov = as.matrix(read.table(file="../Output/BayEnv/Y2017_matrix_18k.txt",header=F))
mat = cov2cor(cov)
row.names(mat)<-pops2
colnames(mat)<-pops2
mat<-mat[,c("TB17","PWS17","SS17","BC17","WA17","CA17")]
mat<-mat[c("TB17","PWS17","SS17","BC17","WA17","CA17"),]

mat2<-mat
diag(mat2)<-NA
mat2[upper.tri(mat2)]<-NA
mat2m<-melt(mat2)

mat2m$Var1<-factor(mat2m$Var1, levels=c("TB17","PWS17","SS17","BC17","WA17","CA17"))
mat2m$Var2<-factor(mat2m$Var2, levels=c("TB17","PWS17","SS17","BC17","WA17","CA17"))

mat2m$color<-"a"
mat2m$color[mat2m$value>0.008]<-"b"
ggplot(mat2m, aes(x=Var1, y=Var2, fill=value))+
    geom_tile(color = "white")+
    scale_fill_gradientn(colors=c("#073466","white"), limits=c(min(mat2m$value, na.rm=T), max(mat2m$value, na.rm=T)+0.01), na.value="gray90", 
                         name="Correlation")+
    theme_minimal()+ xlab("")+ylab("")+
    geom_text(aes(Var1, Var2, label = round(value, digits = 3), color=color),  size = 5)+
    scale_color_manual(values=c("white","black"), guide='none')

ggsave("../Output/BayEnv/Correlation_plot_18705snps.png", width = 6, height = 4.5, dpi=300)    

* 18K Snps produced weirder results than 8K snps

6 Run BayEnv2 matrix covariance estimations 10 times using 8621 SNPs and average the results

# Run BayEnv2 for all SNPS with environmental data
for (i in 1:10){
    sink(paste0("../Data/Slurmscripts/bayenv_run_rep",i,".sh"))
    cat("#!/bin/bash -l\n")
    cat(paste0("#SBATCH --job-name=bayenv",i," \n"))
    cat(paste0("#SBATCH --mem=16G \n")) 
    cat(paste0("#SBATCH --ntasks=8 \n")) 
    cat(paste0("#SBATCH -e bayenv",i,".err  \n"))
    cat(paste0("#SBATCH --time=17:00:00  \n"))
    cat(paste0("#SBATCH -p high  \n"))
    cat("\n\n")
    cat("module load bayenv2 \n\n") 
    
    cat(paste0("bayenv2 -i Y2017_filtered_snpsfile_8621.txt -p 6 -k 100000 -r ",i,i,"234","  > Y2017_matrix_rep",i,".out \n"))
    sink(NULL)
}

#pooled version
for (i in 1:10){
    sink(paste0("../Data/Slurmscripts/bayenv_run_pool_rep",i,".sh"))
    cat("#!/bin/bash -l\n")
    cat(paste0("#SBATCH --job-name=bePool",i," \n"))
    cat(paste0("#SBATCH --mem=16G \n")) 
    cat(paste0("#SBATCH --ntasks=8 \n")) 
    cat(paste0("#SBATCH -e bePool",i,".err  \n"))
    cat(paste0("#SBATCH --time=72:00:00  \n"))
    cat(paste0("#SBATCH -p high  \n"))
    cat("\n\n")
    cat("module load bayenv2 \n\n") 
    
    cat(paste0("bayenv2 -i Y2017_filtered_snpsfile_8621.txt -s samplesize.txt -x -p 6 -k 200000 -r ",i,"234",i,"  > Y2017_matrix_pool_rep",i,".out \n"))
    sink(NULL)
}

7 Obtain the mean cov matrix from 10 replicates (non-pooled method)

  • Non-pooled method creates much larger correlation coefficients
# First extract the last cov matrix
sink("../Output/BayEnv/extract_last_matrix.sh")
for (i in 1: 10){
    cat(paste0("awk '$0 == \"VAR-COVAR MATRIX: ITER = 100000\" {i=1;next};i && i++ <= 6' Y2017_matrix_rep",i,".out > Y2017_matrix_rep",i,".txt \n"))
}
sink(NULL)

#average the 10 covariance matrices
Rep<-list()
for (i in 1:10){
    Rep[[i]]<-as.matrix(read.table(paste0("../Output/BayEnv/Y2017_matrix_rep",i,".txt"),header=F))
    
}
mean_mat = apply(simplify2array(Rep), c(1,2), mean)
write.table(mean_mat, "../Output/BayEnv/Y2017_mean_matrix.txt", quote = F, row.names = F, col.names = F, sep="\t")

mean_mat<-as.matrix(read.table("../Output/BayEnv/Y2017_mean_matrix.txt", header = F))
mat = cov2cor(mean_mat)
row.names(mat)<-pops2
colnames(mat)<-pops2
mat<-mat[,c("TB17","PWS17","SS17","BC17","WA17","CA17")]
mat<-mat[c("TB17","PWS17","SS17","BC17","WA17","CA17"),]

{pdf("../Output/BayEnv/Corplot_mean10_y2017_8621snps.pdf", width = 5, height=5)
corrplot(mat, is.corr=T)
dev.off()}

mat2<-mat
diag(mat2)<-NA
mat2[upper.tri(mat2)]<-NA
mat2m<-melt(mat2)

mat2m$Var1<-factor(mat2m$Var1, levels=c("TB17","PWS17","SS17","BC17","WA17","CA17"))
mat2m$Var2<-factor(mat2m$Var2, levels=c("TB17","PWS17","SS17","BC17","WA17","CA17"))

mat2m$color<-"a"
mat2m$color[mat2m$value>0.008]<-"b"
ggplot(mat2m, aes(x=Var1, y=Var2, fill=value))+
    geom_tile(color = "white")+
     scale_fill_gradientn(colors=c("#073466","white"), limits=c(min(mat2m$value, na.rm=T), max(mat2m$value, na.rm=T)+0.01), na.value="gray90", 
                         name="Correlation")+
    theme_minimal()+ xlab("")+ylab("")+
    geom_text(aes(Var1, Var2, label = round(value, digits = 3), color=color),  size = 5)+
    scale_color_manual(values=c("white","black"), guide='none')

ggsave("../Output/BayEnv/Correlation_mean_plot_8621snps.png", width = 6, height = 4.5, dpi=300)    

7.1 Covariance matricies from the pooled NGS setting

Pool<-list()
for (i in 1:2){
    Pool[[i]]<-as.matrix(read.table(paste0("../Output/BayEnv/Y2017_matrix_pool_rep",i,".txt"),header=F))
    
}
mean_mat = apply(simplify2array(Pool), c(1,2), mean)
#write.table(mean_mat, "../Output/BayEnv/Y2017_mean_pool_matrix.txt", quote = F, row.names = F, col.names = F, sep="\t")

mat = cov2cor(mean_mat)
row.names(mat)<-pops2
colnames(mat)<-pops2
mat<-mat[,c("TB17","PWS17","SS17","BC17","WA17","CA17")]
mat<-mat[c("TB17","PWS17","SS17","BC17","WA17","CA17"),]

mat2<-mat
diag(mat2)<-NA
mat2[upper.tri(mat2)]<-NA
mat2m<-melt(mat2)
mat2m$Var1<-factor(mat2m$Var1, levels=c("TB17","PWS17","SS17","BC17","WA17","CA17"))
mat2m$Var2<-factor(mat2m$Var2, levels=c("TB17","PWS17","SS17","BC17","WA17","CA17"))
mat2m$color<-"a"
mat2m$color[mat2m$value>0.01]<-"b"
ggplot(mat2m, aes(x=Var1, y=Var2, fill=value))+
    geom_tile(color = "white")+
     scale_fill_gradientn(colors=c("#073466","white"), limits=c(min(mat2m$value, na.rm=T), max(mat2m$value, na.rm=T)+0.01), na.value="gray90", 
                         name="Correlation")+
    theme_minimal()+ xlab("")+ylab("")+
    geom_text(aes(Var1, Var2, label = round(value, digits = 3), color=color),  size = 4)+
    scale_color_manual(values=c("white","black"), guide='none')

ggsave("../Output/BayEnv/Correlation_mean_pooled_plot_8621snps.png", width = 6, height = 4.5, dpi=300)   

7.2 Run BayEnv2 with the environmental variable file

  • bayenv_run_env2.sh (Using the mean matrix from 9 replicates (non-pool))
bf<-read.table("../Output/BayEnv/Mean9.bf", header = F)
colnames(bf)<-c("batch_id", "BF","rho","r")

# List all split file names:
# ls snp_batch* >snp_filenames.txt

names<-read.table("../Output/BayEnv/snp_filenames.txt")

#snp names
snps<-read.csv("../Output/BayEnv/Y2017_filtered_snpsfile_8621.csv")
snps_name<-snps[seq(1,nrow(snps),by=2),c("chr","pos")]
snps_name$batch_id<-names$V1

# now merge the two tables
bf<-merge(snps_name, bf, by="batch_id")
bf$chrom<-as.integer(gsub("chr",'',bf$chr))
bf<-bf[order(bf$chrom, bf$pos),]
bf$n<-1:nrow(bf)
evens<-paste0("chr",seq(2,26, by=2))
bf$color<-"a"
bf$color[bf$chr %in% evens]<-"b"
ggplot(bf, aes(x=n,y=BF, color=color))+xlab('')+
    geom_point(size=0.5, alpha=0.6)+ylab("Bayes facator")+
    theme_classic()+
    scale_color_manual(values=c("gray70","steelblue"), guide="none")+
    theme(axis.text.x = element_blank())
ggsave("../Output/baysFactor_acrossGenome_Y2017_run1.png", width = 7, height = 3, dpi=300)


#remove the 2 outliers & log scale
ggplot(bf, aes(x=n,y=BF, color=color))+xlab('')+
    geom_point(size=0.5, alpha=0.6)+ylab("Bayes facator")+
    theme_classic()+
    scale_color_manual(values=c("gray70","steelblue"), guide="none")+
    theme(axis.text.x = element_blank(), axis.ticks.x=element_blank())+
    scale_y_continuous(labels = label_comma(), trans = 'log10', limits=c(0.05,1000))
ggsave("../Output/baysFactor_acrossGenome_Y2017_run1_logscaled.png", width = 7, height = 3, dpi=300)

#rho values
ggplot(bf, aes(x=n,y=rho, color=color))+xlab('')+
    geom_point(size=0.5, alpha=0.6)+ylab("Speaerman's rho")+
    theme_classic()+
    scale_color_manual(values=c("gray70","steelblue"), guide="none")+
    theme(axis.text.x = element_blank(), axis.ticks.x=element_blank())
ggsave("../Output/Rho_acrossGenome_Y2017_run1.png", width = 7, height = 3, dpi=300)
snp2017<-function(df, x){
    s17<-df[,c(1:2,grep("17",colnames(df)))]
    remove<-which(rowSums(s17[,3:8])<=3)
    #check if even or odd numbers
    even<-remove[sapply(remove, function(x) x%%2==0)]
    remove_odd<-remove[!(remove %in% even)]
    rem<-c(remove_odd, remove_odd+1, even, even-1)
    rem<-rem[order(rem)]
    s17new<-s17[-rem,]
    return(s17new)
}

snps2017_25<-snp2017(snps2)
table(snps2017_25$chr)
LS0tCnRpdGxlOiAiUnVuIEJheUVudjIiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgICB0b2M6IHRydWUgCiAgICAgIHRvY19mbG9hdDogdHJ1ZQogICAgICBudW1iZXJfc2VjdGlvbnM6IFRydWUKICAgICAgdGhlbWU6IGx1bWVuCiAgICAgIGhpZ2hsaWdodDogdGFuZ28KICAgICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICAgIGRmX3ByaW50OiBwYWdlZAotLS0KYGBge3IgZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0Kc291cmNlKCIuLi9Sc2NyaXB0cy9CYXNlU2NyaXB0cy5SIikKbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KERhdGFDb21iaW5lKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShzY2FsZXMpCmxpYnJhcnkoY29ycnBsb3QpCmxpYnJhcnkoZ3Bsb3RzKQpwb3BzLmluZm88LXJlYWQuY3N2KCIuLi9EYXRhL1NhbXBsZV9tZXRhZGF0YV84OTJwb3BzLmNzdiIpCnBvcHM8LXVuaXF1ZShwb3BzLmluZm8kUG9wdWxhdGlvbi5ZZWFyKQpwb3BzMjwtcG9wc1tncmVwKCIxNyIsIHBvcHMpXQpgYGAKCiMgUHJlcGFyZSBTTlBzIGZvciBCYXlFbnYKIyMgUHJ1bmUgdGhlIFZDRiBmaWxlIHdpdGggdGhlIHN0cmljdCBzdGFuZGFyZCBhbmQgZmlsdGVyIHRoZSBsb2NpIAoKYGBge2Jhc2ggZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KI2F0IGZhcm0gKHBydW5lX3ZjZl9mcm9CYXlFbnYuc2gpCm1vZHVsZSBsb2FkIHBsaW5rCiNhZGQgdmFyaWFudCBJRCBuYW1lcyBmb3IgcHJ1bmluZyAKcGxpbmsgLS12Y2YgL2hvbWUva3Rpc3QvcGgvZGF0YS9uZXdfdmNmL01ENzAwMC9QSF9EUDYwMF83MDAwX21pblEyMF9taW5NUTMwX05TMC41X21hZjA1LnZjZi5neiAtLXNldC1taXNzaW5nLXZhci1pZHMgQDojW3BoXSAtLW1ha2UtYmVkIC0tb3V0IC9ob21lL2t0aXN0L3BoL2RhdGEvbmV3X3ZjZi9NRDcwMDAvcGxpbmtmaWxlcy9NRDcwMDBfbWFmMC4wNSAKCnBsaW5rIC0tYmZpbGUgL2hvbWUva3Rpc3QvcGgvZGF0YS9uZXdfdmNmL01ENzAwMC9wbGlua2ZpbGVzL01ENzAwMF9tYWYwLjA1ICAtLXJlY29kZSAtLXRhYiAtLW91dCAvaG9tZS9rdGlzdC9waC9kYXRhL25ld192Y2YvTUQ3MDAwL3BsaW5rZmlsZXMvTUQ3MDAwX21hZjAuMDUgCgpwbGluayAtLWZpbGUgIC9ob21lL2t0aXN0L3BoL2RhdGEvbmV3X3ZjZi9NRDcwMDAvcGxpbmtmaWxlcy9NRDcwMDBfbWFmMC4wNSAgLS1pbmRlcC1wYWlyd2lzZSAxMDAwa2IgMSAwLjE1IC0tb3V0IC9ob21lL2t0aXN0L3BoL2RhdGEvbmV3X3ZjZi9NRDcwMDAvcGxpbmtmaWxlcy9wcnVuZV9zdHJpY3RfMTAwMGtfMC4xNQoKI0RpZmZlcmVudCBwYXJhbWV0ZXJzICh3aW5kb3cgc2l6ZSBhbmQgUjIgbnVtYmVyKSBkaWQgbm90IGNoYW5nZSB0aGUgbnVtYmVyIG9mIHBydW5lZCBsb2NpIGRyYXN0aWNhbGx5OgojIGluZGVwLXBhaXJ3aXNlIDUwIDUgMC41IChvcmlnaW5hbCBwYXJhbWV0ZXJzKSA9IDI3MDAwMCBsb2NpCiMgc3RyaWN0ZXIgcGFyYW1ldGVycyAxMDAwayAxIDAuMTUgICA9IDIwNTcyNgoKIyBGaWx0ZXIgdGhlIHZjZiBmaWxlIChwcnVuZV9jb252ZXJ0X0JBWUVOVi5zaCkKIyBGaXJzdCwgcmVmb3JtYXQgdGhlIHBydW5lLmluICBmaWxlIGFzIGEgJ0NIUicgJ1BPUycgdGFiLWRlbGltaXRlZCB0eHQgCnNlZCAtZSBzL1xcW3BoXFxdLy9nIHBydW5lX3N0cmljdF8xMDAwa18wLjE1LnBydW5lLmluIHwgYXdrICAne2dzdWIoIjoiLCJcdCIsJDApOyBwcmludDt9JyA+IHBydW5lX3N0cmljdF8xMDAwa18wLjE1LmluLnR4dAoKIyBSZW1vdmUgdGhlIHBydW5lZCBsb2NpCmJjZnRvb2xzIHZpZXcgLVIgL2hvbWUva3Rpc3QvcGgvZGF0YS9uZXdfdmNmL01ENzAwMC9wbGlua2ZpbGVzL3BydW5lX3N0cmljdF8xMDAwa18wLjE1LmluLnR4dCAvaG9tZS9rdGlzdC9waC9kYXRhL25ld192Y2YvTUQ3MDAwL1BIX0RQNjAwXzcwMDBfbWluUTIwX21pbk1RMzBfTlMwLjVfbWFmMDUudmNmLmd6ID4gL2hvbWUva3Rpc3QvcGgvZGF0YS9uZXdfdmNmL01ENzAwMC9wbGlua2ZpbGVzL3BydW5lZF9zdHJpY3RfUEhfYWxsX21hZjA1LnZjZgoKYmd6aXAgL2hvbWUva3Rpc3QvcGgvZGF0YS9uZXdfdmNmL01ENzAwMC9wbGlua2ZpbGVzL3BydW5lZF9zdHJpY3RfUEhfYWxsX21hZjA1LnZjZgoKCiMgU3Vic2V0IHRoZSBlYWNoIGZpbGUgKHN1YnNldF9wcnVuZWRWQ0YyLnNoKQpiY2Z0b29scyB2aWV3IC1PeiAtUyAvaG9tZS9rdGlzdC9waC9kYXRhL25ld192Y2YvcG9wdWxhdGlvbi9CQzE3LnR4dCAtLXRocmVhZHMgMjQgIC9ob21lL2t0aXN0L3BoL2RhdGEvbmV3X3ZjZi9NRDcwMDAvcGxpbmtmaWxlcy9wcnVuZWRfc3RyaWN0X1BIX2FsbF9tYWYwNS52Y2YuZ3ogPiAvaG9tZS9rdGlzdC9waC9kYXRhL25ld192Y2YvTUQ3MDAwL3BsaW5rZmlsZXMvUHJ1bmVkX0JDMTdfbWFmMDUudmNmLmd6CmJjZnRvb2xzIGluZGV4IC9ob21lL2t0aXN0L3BoL2RhdGEvbmV3X3ZjZi9NRDcwMDAvcGxpbmtmaWxlcy9QcnVuZWRfQkMxN19tYWYwNS52Y2YuZ3ogCmJjZnRvb2xzIHZpZXcgLU96IC1TIC9ob21lL2t0aXN0L3BoL2RhdGEvbmV3X3ZjZi9wb3B1bGF0aW9uL0NBMTcudHh0IC0tdGhyZWFkcyAyNCAgL2hvbWUva3Rpc3QvcGgvZGF0YS9uZXdfdmNmL01ENzAwMC9wbGlua2ZpbGVzL3BydW5lZF9zdHJpY3RfUEhfYWxsX21hZjA1LnZjZi5neiA+IC9ob21lL2t0aXN0L3BoL2RhdGEvbmV3X3ZjZi9NRDcwMDAvcGxpbmtmaWxlcy9QcnVuZWRfQ0ExN19tYWYwNS52Y2YuZ3oKYmNmdG9vbHMgaW5kZXggL2hvbWUva3Rpc3QvcGgvZGF0YS9uZXdfdmNmL01ENzAwMC9wbGlua2ZpbGVzL1BydW5lZF9DQTE3X21hZjA1LnZjZi5neiAKYmNmdG9vbHMgdmlldyAtT3ogLVMgL2hvbWUva3Rpc3QvcGgvZGF0YS9uZXdfdmNmL3BvcHVsYXRpb24vUFdTMDcudHh0IC0tdGhyZWFkcyAyNCAgL2hvbWUva3Rpc3QvcGgvZGF0YS9uZXdfdmNmL01ENzAwMC9wbGlua2ZpbGVzL3BydW5lZF9zdHJpY3RfUEhfYWxsX21hZjA1LnZjZi5neiA+IC9ob21lL2t0aXN0L3BoL2RhdGEvbmV3X3ZjZi9NRDcwMDAvcGxpbmtmaWxlcy9QcnVuZWRfUFdTMDdfbWFmMDUudmNmLmd6CmJjZnRvb2xzIGluZGV4IC9ob21lL2t0aXN0L3BoL2RhdGEvbmV3X3ZjZi9NRDcwMDAvcGxpbmtmaWxlcy9QcnVuZWRfUFdTMDdfbWFmMDUudmNmLmd6IApiY2Z0b29scyB2aWV3IC1PeiAtUyAvaG9tZS9rdGlzdC9waC9kYXRhL25ld192Y2YvcG9wdWxhdGlvbi9QV1MxNy50eHQgLS10aHJlYWRzIDI0ICAvaG9tZS9rdGlzdC9waC9kYXRhL25ld192Y2YvTUQ3MDAwL3BsaW5rZmlsZXMvcHJ1bmVkX3N0cmljdF9QSF9hbGxfbWFmMDUudmNmLmd6ID4gL2hvbWUva3Rpc3QvcGgvZGF0YS9uZXdfdmNmL01ENzAwMC9wbGlua2ZpbGVzL1BydW5lZF9QV1MxN19tYWYwNS52Y2YuZ3oKYmNmdG9vbHMgaW5kZXggL2hvbWUva3Rpc3QvcGgvZGF0YS9uZXdfdmNmL01ENzAwMC9wbGlua2ZpbGVzL1BydW5lZF9QV1MxN19tYWYwNS52Y2YuZ3ogCmJjZnRvb2xzIHZpZXcgLU96IC1TIC9ob21lL2t0aXN0L3BoL2RhdGEvbmV3X3ZjZi9wb3B1bGF0aW9uL1BXUzkxLnR4dCAtLXRocmVhZHMgMjQgIC9ob21lL2t0aXN0L3BoL2RhdGEvbmV3X3ZjZi9NRDcwMDAvcGxpbmtmaWxlcy9wcnVuZWRfc3RyaWN0X1BIX2FsbF9tYWYwNS52Y2YuZ3ogPiAvaG9tZS9rdGlzdC9waC9kYXRhL25ld192Y2YvTUQ3MDAwL3BsaW5rZmlsZXMvUHJ1bmVkX1BXUzkxX21hZjA1LnZjZi5negpiY2Z0b29scyBpbmRleCAvaG9tZS9rdGlzdC9waC9kYXRhL25ld192Y2YvTUQ3MDAwL3BsaW5rZmlsZXMvUHJ1bmVkX1BXUzkxX21hZjA1LnZjZi5neiAKYmNmdG9vbHMgdmlldyAtT3ogLVMgL2hvbWUva3Rpc3QvcGgvZGF0YS9uZXdfdmNmL3BvcHVsYXRpb24vUFdTOTYudHh0IC0tdGhyZWFkcyAyNCAgL2hvbWUva3Rpc3QvcGgvZGF0YS9uZXdfdmNmL01ENzAwMC9wbGlua2ZpbGVzL3BydW5lZF9zdHJpY3RfUEhfYWxsX21hZjA1LnZjZi5neiA+IC9ob21lL2t0aXN0L3BoL2RhdGEvbmV3X3ZjZi9NRDcwMDAvcGxpbmtmaWxlcy9QcnVuZWRfUFdTOTZfbWFmMDUudmNmLmd6CmJjZnRvb2xzIGluZGV4IC9ob21lL2t0aXN0L3BoL2RhdGEvbmV3X3ZjZi9NRDcwMDAvcGxpbmtmaWxlcy9QcnVuZWRfUFdTOTZfbWFmMDUudmNmLmd6IApiY2Z0b29scyB2aWV3IC1PeiAtUyAvaG9tZS9rdGlzdC9waC9kYXRhL25ld192Y2YvcG9wdWxhdGlvbi9TUzA2LnR4dCAtLXRocmVhZHMgMjQgIC9ob21lL2t0aXN0L3BoL2RhdGEvbmV3X3ZjZi9NRDcwMDAvcGxpbmtmaWxlcy9wcnVuZWRfc3RyaWN0X1BIX2FsbF9tYWYwNS52Y2YuZ3ogPiAvaG9tZS9rdGlzdC9waC9kYXRhL25ld192Y2YvTUQ3MDAwL3BsaW5rZmlsZXMvUHJ1bmVkX1NTMDZfbWFmMDUudmNmLmd6CmJjZnRvb2xzIGluZGV4IC9ob21lL2t0aXN0L3BoL2RhdGEvbmV3X3ZjZi9NRDcwMDAvcGxpbmtmaWxlcy9QcnVuZWRfU1MwNl9tYWYwNS52Y2YuZ3ogCmJjZnRvb2xzIHZpZXcgLU96IC1TIC9ob21lL2t0aXN0L3BoL2RhdGEvbmV3X3ZjZi9wb3B1bGF0aW9uL1NTMTcudHh0IC0tdGhyZWFkcyAyNCAgL2hvbWUva3Rpc3QvcGgvZGF0YS9uZXdfdmNmL01ENzAwMC9wbGlua2ZpbGVzL3BydW5lZF9zdHJpY3RfUEhfYWxsX21hZjA1LnZjZi5neiA+IC9ob21lL2t0aXN0L3BoL2RhdGEvbmV3X3ZjZi9NRDcwMDAvcGxpbmtmaWxlcy9QcnVuZWRfU1MxN19tYWYwNS52Y2YuZ3oKYmNmdG9vbHMgaW5kZXggL2hvbWUva3Rpc3QvcGgvZGF0YS9uZXdfdmNmL01ENzAwMC9wbGlua2ZpbGVzL1BydW5lZF9TUzE3X21hZjA1LnZjZi5neiAKYmNmdG9vbHMgdmlldyAtT3ogLVMgL2hvbWUva3Rpc3QvcGgvZGF0YS9uZXdfdmNmL3BvcHVsYXRpb24vU1M5Ni50eHQgLS10aHJlYWRzIDI0ICAvaG9tZS9rdGlzdC9waC9kYXRhL25ld192Y2YvTUQ3MDAwL3BsaW5rZmlsZXMvcHJ1bmVkX3N0cmljdF9QSF9hbGxfbWFmMDUudmNmLmd6ID4gL2hvbWUva3Rpc3QvcGgvZGF0YS9uZXdfdmNmL01ENzAwMC9wbGlua2ZpbGVzL1BydW5lZF9TUzk2X21hZjA1LnZjZi5negpiY2Z0b29scyBpbmRleCAvaG9tZS9rdGlzdC9waC9kYXRhL25ld192Y2YvTUQ3MDAwL3BsaW5rZmlsZXMvUHJ1bmVkX1NTOTZfbWFmMDUudmNmLmd6IApiY2Z0b29scyB2aWV3IC1PeiAtUyAvaG9tZS9rdGlzdC9waC9kYXRhL25ld192Y2YvcG9wdWxhdGlvbi9UQjA2LnR4dCAtLXRocmVhZHMgMjQgIC9ob21lL2t0aXN0L3BoL2RhdGEvbmV3X3ZjZi9NRDcwMDAvcGxpbmtmaWxlcy9wcnVuZWRfc3RyaWN0X1BIX2FsbF9tYWYwNS52Y2YuZ3ogPiAvaG9tZS9rdGlzdC9waC9kYXRhL25ld192Y2YvTUQ3MDAwL3BsaW5rZmlsZXMvUHJ1bmVkX1RCMDZfbWFmMDUudmNmLmd6CmJjZnRvb2xzIGluZGV4IC9ob21lL2t0aXN0L3BoL2RhdGEvbmV3X3ZjZi9NRDcwMDAvcGxpbmtmaWxlcy9QcnVuZWRfVEIwNl9tYWYwNS52Y2YuZ3ogCmJjZnRvb2xzIHZpZXcgLU96IC1TIC9ob21lL2t0aXN0L3BoL2RhdGEvbmV3X3ZjZi9wb3B1bGF0aW9uL1RCMTcudHh0IC0tdGhyZWFkcyAyNCAgL2hvbWUva3Rpc3QvcGgvZGF0YS9uZXdfdmNmL01ENzAwMC9wbGlua2ZpbGVzL3BydW5lZF9zdHJpY3RfUEhfYWxsX21hZjA1LnZjZi5neiA+IC9ob21lL2t0aXN0L3BoL2RhdGEvbmV3X3ZjZi9NRDcwMDAvcGxpbmtmaWxlcy9QcnVuZWRfVEIxN19tYWYwNS52Y2YuZ3oKYmNmdG9vbHMgaW5kZXggL2hvbWUva3Rpc3QvcGgvZGF0YS9uZXdfdmNmL01ENzAwMC9wbGlua2ZpbGVzL1BydW5lZF9UQjE3X21hZjA1LnZjZi5neiAKYmNmdG9vbHMgdmlldyAtT3ogLVMgL2hvbWUva3Rpc3QvcGgvZGF0YS9uZXdfdmNmL3BvcHVsYXRpb24vVEI5MS50eHQgLS10aHJlYWRzIDI0ICAvaG9tZS9rdGlzdC9waC9kYXRhL25ld192Y2YvTUQ3MDAwL3BsaW5rZmlsZXMvcHJ1bmVkX3N0cmljdF9QSF9hbGxfbWFmMDUudmNmLmd6ID4gL2hvbWUva3Rpc3QvcGgvZGF0YS9uZXdfdmNmL01ENzAwMC9wbGlua2ZpbGVzL1BydW5lZF9UQjkxX21hZjA1LnZjZi5negpiY2Z0b29scyBpbmRleCAvaG9tZS9rdGlzdC9waC9kYXRhL25ld192Y2YvTUQ3MDAwL3BsaW5rZmlsZXMvUHJ1bmVkX1RCOTFfbWFmMDUudmNmLmd6IApiY2Z0b29scyB2aWV3IC1PeiAtUyAvaG9tZS9rdGlzdC9waC9kYXRhL25ld192Y2YvcG9wdWxhdGlvbi9UQjk2LnR4dCAtLXRocmVhZHMgMjQgIC9ob21lL2t0aXN0L3BoL2RhdGEvbmV3X3ZjZi9NRDcwMDAvcGxpbmtmaWxlcy9wcnVuZWRfc3RyaWN0X1BIX2FsbF9tYWYwNS52Y2YuZ3ogPiAvaG9tZS9rdGlzdC9waC9kYXRhL25ld192Y2YvTUQ3MDAwL3BsaW5rZmlsZXMvUHJ1bmVkX1RCOTZfbWFmMDUudmNmLmd6CmJjZnRvb2xzIGluZGV4IC9ob21lL2t0aXN0L3BoL2RhdGEvbmV3X3ZjZi9NRDcwMDAvcGxpbmtmaWxlcy9QcnVuZWRfVEI5Nl9tYWYwNS52Y2YuZ3ogCmJjZnRvb2xzIHZpZXcgLU96IC1TIC9ob21lL2t0aXN0L3BoL2RhdGEvbmV3X3ZjZi9wb3B1bGF0aW9uL1dBMTcudHh0IC0tdGhyZWFkcyAyNCAgL2hvbWUva3Rpc3QvcGgvZGF0YS9uZXdfdmNmL01ENzAwMC9wbGlua2ZpbGVzL3BydW5lZF9zdHJpY3RfUEhfYWxsX21hZjA1LnZjZi5neiA+IC9ob21lL2t0aXN0L3BoL2RhdGEvbmV3X3ZjZi9NRDcwMDAvcGxpbmtmaWxlcy9QcnVuZWRfV0ExN19tYWYwNS52Y2YuZ3oKYmNmdG9vbHMgaW5kZXggL2hvbWUva3Rpc3QvcGgvZGF0YS9uZXdfdmNmL01ENzAwMC9wbGlua2ZpbGVzL1BydW5lZF9XQTE3X21hZjA1LnZjZi5neiAKCiNDb3VudCB0aGUgYWxsZWxlcyAoZXh0cmFjdF9BQy5zaCkKYmNmdG9vbHMgcXVlcnkgLWYgJyVDSFJPTSAgJVBPUyAgJUlORk8vQUMgICVJTkZPL0FOXG4nIC9ob21lL2t0aXN0L3BoL2RhdGEvbmV3X3ZjZi9NRDcwMDAvcGxpbmtmaWxlcy9QcnVuZWRfUFdTMTdfbWFmMDUudmNmLmd6ID4gL2hvbWUva3Rpc3QvcGgvZGF0YS9uZXdfdmNmL01ENzAwMC9wbGlua2ZpbGVzL3BydW5lZF9BQ19QV1MxNy50eHQgCmJjZnRvb2xzIHF1ZXJ5IC1mICclQ0hST00gICVQT1MgICVJTkZPL0FDICAlSU5GTy9BTlxuJyAvaG9tZS9rdGlzdC9waC9kYXRhL25ld192Y2YvTUQ3MDAwL3BsaW5rZmlsZXMvUHJ1bmVkX1BXUzkxX21hZjA1LnZjZi5neiA+IC9ob21lL2t0aXN0L3BoL2RhdGEvbmV3X3ZjZi9NRDcwMDAvcGxpbmtmaWxlcy9wcnVuZWRfQUNfUFdTOTEudHh0IApiY2Z0b29scyBxdWVyeSAtZiAnJUNIUk9NICAlUE9TICAlSU5GTy9BQyAgJUlORk8vQU5cbicgL2hvbWUva3Rpc3QvcGgvZGF0YS9uZXdfdmNmL01ENzAwMC9wbGlua2ZpbGVzL1BydW5lZF9QV1M5Nl9tYWYwNS52Y2YuZ3ogPiAvaG9tZS9rdGlzdC9waC9kYXRhL25ld192Y2YvTUQ3MDAwL3BsaW5rZmlsZXMvcHJ1bmVkX0FDX1BXUzk2LnR4dCAKCmBgYAoKCgojIyBDcmVhdGUgQWxsZWxlIENvdW50IGZpbGUgCiogVXNlZCB0aGUgb3V0cHV0cyBmcm9tIGV4dHJhY3RfQUMuc2ggdG8gZ2V0IHRoZSBhbGxlbGUgY291bnRzIGZvciBlYWNoIHBvcHVsYXRpb24gYXQgZWFjaCBsb2NpCgpgYGB7ciBldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwb3BzLmluZm88LXJlYWQuY3N2KCIuLi9EYXRhL1NhbXBsZV9tZXRhZGF0YV84OTJwb3BzLmNzdiIpCnBvcHM8LXVuaXF1ZShwb3BzLmluZm8kUG9wdWxhdGlvbi5ZZWFyKQoKI1BydW5lZCBwb3NpdGlvbiBpbmZvCnBydW48LXJlYWQudGFibGUoIi4uL0RhdGEvbmV3X3ZjZi9BQy9QcnVuZWRfcG9zaXRpb25zLnR4dCIpCnBydW4kVjE8LXBhc3RlMCgiY2hyIixwcnVuJFYxKQpjb2xuYW1lcyhwcnVuKTwtYygiY2hyIiwicG9zIikKCnNucGZpbGU8LWRhdGEuZnJhbWUobj0xOigyKm5yb3cocHJ1bikpKQpmb3IgKGkgaW4gMTpsZW5ndGgocG9wcykpewogICAgZGY8LXJlYWQudGFibGUocGFzdGUwKCIuLi9EYXRhL25ld192Y2YvQUMvUHJ1bmVkX0FDXyIscG9wc1tpXSwiLnR4dCIpKQogICAgY29sbmFtZXMoZGYpPC1jKCJjaHIiLCJwb3MiLCJBQyIsIkFOIikKICAgICNtYWpvckFDIGRhdGEgZnJhbWUKICAgIG1hajwtdHJhbnNmb3JtKGRmLCBBQz1BTi1BQykKICAgIG5ld2RmPC1yYmluZChtYWpbLDE6M10sIGRmWywxOjNdKQogICAgI3Jlb3JkZXIgdGhlIHJvd3MKICAgIG48LW5yb3coZGYpCiAgICBuZXdkZjI8LW5ld2RmW2tyb25lY2tlcigxOm4sIGMoMCwgbiksICIrIiksIF0KICAgIG5ld2RmMjwtbmV3ZGYyW29yZGVyKG5ld2RmMiRjaHIsbmV3ZGYyJHBvcyksXQogICAgY29sbmFtZXMobmV3ZGYyKVszXTwtcG9wc1tpXQogICAgI2NvbWJpbmUgdGhlIGRhdGEKICAgIGlmIChpPT0xKSBzbnBmaWxlPC1jYmluZChzbnBmaWxlLG5ld2RmMikKICAgIGlmIChpIT0xKSBzbnBmaWxlPC1jYmluZChzbnBmaWxlLCBuZXdkZjJbLDNdKQp9Cgpjb2xuYW1lcyhzbnBmaWxlKVs1OjE3XTwtcG9wc1syOjE0XQojc2F2ZSB0aGUgc25wZmlcZQpzbnBzPC1zbnBmaWxlWywtMV0KCiNmaWx0ZXIgb3V0IHRoZSBsb3cgYWxsZWxlIGNvdW50IGxvY2kgKDEtMTApCnJlbW92ZTwtd2hpY2gocm93U3VtcyhzbnBzWywzOjE2XSk8PTEwKSAjMjE0MyBsb2NpCmV2ZW48LXJlbW92ZVtzYXBwbHkocmVtb3ZlLCBmdW5jdGlvbih4KSB4JSUyPT0wKV0gI2FsbCBvZGQgbnVtYmVycwpyZW1vdmU8LXVubmFtZShyZW1vdmUpCnJlbW92ZTwtYyhyZW1vdmUsIHJlbW92ZSsxKQpyZW1vdmU8LXJlbW92ZVtvcmRlcihyZW1vdmUpXQpzbnBzMjwtc25wc1stcmVtb3ZlLF0KCndyaXRlLmNzdihzbnBzMiwgIi4uL091dHB1dC9CYXlFbnYvUHJ1bmVkX2FsbFBvcHNfc25wc2ZpbGUuY3N2IikKd3JpdGUudGFibGUoc25wczJbLDM6MTZdLCAiLi4vT3V0cHV0L0JheUVudi9QcnVuZWRfYWxsUG9wc19zbnBzZmlsZS50eHQiLCBzZXA9Ilx0Iiwgcm93Lm5hbWVzID0gRixjb2wubmFtZXMgPSBGLCBxdW90ZT1GKQoKYGBgCgoKIyMgRmlsdGVyIGxvY2kgd2l0aCBsb3cgcmVwcmVzZW50YXRpb24gYXMgMjAwayBzbnBzIGFyZSB0b28gbWFueSAKCmBgYHtyIGV2YWw9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgRmlsdGVyIG1vcmUgbG9jaSBzaW5jZSAyMDBrIGlzIHRvbyBtYW55Lgojc25wczI8LXJlYWQuY3N2KCIuLi9PdXRwdXQvQmF5RW52L1BydW5lZF9hbGxQb3BzX3NucHNmaWxlLmNzdiIsIHJvdy5uYW1lcyA9IDEpCgoKIyBUaGlzIGZ1bmN0aW9uIGZpbHRlcnMgYSBzbnAgZmlsZSBiYXNlZCBvbiB0aGUgdG90YWwgYWxsZWxlIGNvdW50IChvZiBtaW5vciBhbGxlbGVzKQojaW5wdXQgZGYgPVssYygiY2hyLCJwb3MiLCBwb3AxLHBvcDIsLi4uKV0KdHJpbXNucDwtZnVuY3Rpb24oeCwgZGYpewogICAgcmVtb3ZlPC13aGljaChyb3dTdW1zKGRmWywzOm5jb2woZGYpXSk8PXgpIAogICAgcmVtb3ZlX2V2ZW48LXJlbW92ZVtzYXBwbHkocmVtb3ZlLCBmdW5jdGlvbih4KSB4JSUyPT0wKV0KICAgIHJlbW92ZV9vZGQ8LXJlbW92ZVshKHJlbW92ZSAlaW4lIHJlbW92ZV9ldmVuKV0KICAgIHJlbXY8LWMocmVtb3ZlX29kZCwgcmVtb3ZlX29kZCsxLCByZW1vdmVfZXZlbiwgcmVtb3ZlX2V2ZW4tMSkKICAgIHNucHNOZXc8LWRmWy1yZW12LF0KICAgIHJldHVybihzbnBzTmV3KQp9CgpzbnBzXzI1PC10cmltc25wKDQ5Miwgc25wczIpICMxMzY0Ny8yIGxvY2kgCnNucHNfMjA8LXRyaW1zbnAoMTc4LCBzbnBzMikgIzk5NTM2LzIgbG9jaQoKI3dyaXRlLnRhYmxlKHNucHNfMjVbLDM6MTZdLCAiLi4vT3V0cHV0L0JheUVudi9QcnVuZWRfYWxsUG9wc18yNVBlcmNlbnRfc25wc2ZpbGUudHh0Iiwgc2VwPSJcdCIsIHJvdy5uYW1lcyA9IEYsY29sLm5hbWVzID0gRiwgcXVvdGU9RikKI3dyaXRlLnRhYmxlKHNucHNfMjBbLDM6MTZdLCAiLi4vT3V0cHV0L0JheUVudi9QcnVuZWRfYWxsUG9wc18yMFBlcmNlbnRfc25wc2ZpbGUudHh0Iiwgc2VwPSJcdCIsIHJvdy5uYW1lcyA9IEYsY29sLm5hbWVzID0gRiwgcXVvdGU9RikKCgoKI0NyZWF0ZSB5ZWFyIDIwMTcgb25seSBmaWxlCiNzbnBzMjwtcmVhZC5jc3YoIi4uL091dHB1dC9CYXlFbnYvUHJ1bmVkX2FsbFBvcHNfc25wc2ZpbGUuY3N2Iiwgcm93Lm5hbWVzID0gMSkKc25wczE3PC1zbnBzMlssYygxLDIsZ3JlcCgiMTciLGNvbG5hbWVzKHNucHMyKSkpXQoKCiMgQ3JlYXRlIDIwMTcgZmlsZSB3aXRoIGNocm9tb3NvbWUgYW5kIHBvcCBiYXNlZCBmaWx0ZXJpbmcKcG9wczI8LXBvcHNbZ3JlcCgiMTciLCBwb3BzKV0KIyAxLiBjcmVhdGUgYW4gYWxsZWxlIGRlcHRoIHRhYmxlIApzbnBmaWxlPC1kYXRhLmZyYW1lKG49MTpucm93KHBydW4pKQpmb3IgKGkgaW4gMTpsZW5ndGgocG9wczIpKXsKICAgIGRmPC1yZWFkLnRhYmxlKHBhc3RlMCgiLi4vRGF0YS9uZXdfdmNmL0FDL1BydW5lZF9BQ18iLHBvcHMyW2ldLCIudHh0IikpCiAgICBjb2xuYW1lcyhkZik8LWMoImNociIsInBvcyIsIkFDIixwb3BzMltpXSkKICAgIGlmIChpPT0xKSBzbnBmaWxlPC1jYmluZChzbnBmaWxlLGRmWyxjKDEsMiw0KV0pCiAgICBpZiAoaSE9MSkgewogICAgICAgIHNucGZpbGU8LWNiaW5kKHNucGZpbGUsIGRmWyw0XSkKICAgICAgICBjb2xuYW1lcyhzbnBmaWxlKVsoaSszKV08LXBvcHMyW2ldfQp9CgojcG9wIHNpemUKcG9wc2l6ZTwtcmVhZC5jc3YoIi4uL0RhdGEvU2FtcGxlc2l6ZS5wZXIucG9wLmNzdiIpCgojIFNldCB0aGUgbWluaW11bSBjdXRvZmYgZm9yIGFsbGVsZSBjb3VudCB0b3RhbCBwZXIgcG9wdWxhdGlvbiAoNzAlKQpwb3BzaXplJGN1dG9mZjwtYXMuaW50ZWdlcihwb3BzaXplJEZyZXEqMiouNykKcG9wbjwtcG9wc2l6ZSRjdXRvZmZbcG9wc2l6ZSRWYXIxICVpbiVwb3BzMl0KCiMgc3Vic3RyYWN0IHRoZSBjdXRvZmYgYWxsZWxlIGNvdW50CnNucGZpbGUzPC1zbnBmaWxlWyw0OjldLXBvcG4Kc25wZmlsZTM8LWNiaW5kKHNucGZpbGVbLDI6M10sIHNucGZpbGUzKQojIHRoaXMgY29udGFpbnMgaWYgdGhlIGxvY2kgaW4gYSBwb3AgYXJlIGFib3ZlIChwb3N0aXZlKSBvciBiZWxvdyAobmVnYXRpdmUpIHRoZSBjdXRvZmYgYWxsZWxlIGRlcHRoIAoKaGFzX25lZ2F0aXZlczwtYXBwbHkoc25wZmlsZTNbLDM6OF0sIDEsIGZ1bmN0aW9uKHgpIGFueSh4PDApKQpsZW5ndGgoaGFzX25lZ2F0aXZlc1toYXNfbmVnYXRpdmVzPT1GXSkKIzE4OTA4IGxvY2kgaGFzID43MCUgb2YgaW5kaXZpZHVhbHMgcmVwcmVzZW50ZWQgaW4gZWFjaCBwb3AKCiNmaWx0ZXJlZCB0byAxODkwOCBsb2NpCnBvc2l0aXZlczwtd2hpY2goaGFzX25lZ2F0aXZlcz09RikKZmlsdGVyZWQ8LXNucGZpbGUzW3Bvc2l0aXZlcyxdCgp0YWJsZShmaWx0ZXJlZCRjaHIpCiMgY2hyMSBjaHIxMCBjaHIxMSBjaHIxMiBjaHIxMyBjaHIxNCBjaHIxNSBjaHIxNiBjaHIxNyBjaHIxOCBjaHIxOSAgY2hyMiBjaHIyMCAKIyAgODcxICAgNTcyICAgNjE3ICAgNjQ1ICAgNTcxICAgNTkxICAgOTgyICAgNzQ4ICAgNTkwICAxNTE4ICAgNTgxICAxNDY4ICAgNDMxIAojY2hyMjEgY2hyMjIgY2hyMjMgY2hyMjUgY2hyMjYgIGNocjMgIGNocjQgIGNocjUgIGNocjYgIGNocjcgIGNocjggIGNocjkgCiMgIDQ3MSAgIDU3MyAgIDg0OSAgIDcxMCAgIDgzNyAgIDYyNCAgIDcxOSAgIDQ4MSAgIDgzMiAgMTE0NSAgIDU2MSAgIDkyMSAKICNjaHIgMiBoYXMgdGhlIGhpZ2hlc3QgbnVtYmVyIG9mIGxvY2kKCmZpbHRlcmVkX3NucGZpbGU8LWRhdGEuZnJhbWUobj0xOigyKm5yb3coZmlsdGVyZWQpKSkKZm9yIChpIGluIDE6bGVuZ3RoKHBvcHMyKSl7CiAgICBkZjwtcmVhZC50YWJsZShwYXN0ZTAoIi4uL0RhdGEvbmV3X3ZjZi9BQy9QcnVuZWRfQUNfIixwb3BzMltpXSwiLnR4dCIpKQogICAgY29sbmFtZXMoZGYpPC1jKCJjaHIiLCJwb3MiLCJBQyIsIkFOIikKICAgIGRmPC1tZXJnZShmaWx0ZXJlZFssMToyXSwgZGYsIGJ5PWMoImNociIsInBvcyIpKQogICAgI21ham9yQUMgZGF0YSBmcmFtZQogICAgbWFqPC10cmFuc2Zvcm0oZGYsIEFDPUFOLUFDKQogICAgbmV3ZGY8LXJiaW5kKG1halssMTozXSwgZGZbLDE6M10pCiAgICAjcmVvcmRlciB0aGUgcm93cwogICAgbjwtbnJvdyhkZikKICAgIG5ld2RmMjwtbmV3ZGZba3JvbmVja2VyKDE6biwgYygwLCBuKSwgIisiKSwgXQogICAgbmV3ZGYyPC1uZXdkZjJbb3JkZXIobmV3ZGYyJGNocixuZXdkZjIkcG9zKSxdCiAgICAjY29tYmluZSB0aGUgZGF0YQogICAgaWYgKGk9PTEpIGZpbHRlcmVkX3NucGZpbGU8LWNiaW5kKGZpbHRlcmVkX3NucGZpbGUsbmV3ZGYyKQogICAgaWYgKGkhPTEpIGZpbHRlcmVkX3NucGZpbGU8LWNiaW5kKGZpbHRlcmVkX3NucGZpbGUsIG5ld2RmMlssM10pCiAgICBjb2xuYW1lcyhmaWx0ZXJlZF9zbnBmaWxlKVtpKzNdPC1wb3BzMltpXQp9CgpmaWx0ZXJlZF9zbnBmaWxlPC1maWx0ZXJlZF9zbnBmaWxlWywtMV0KCm5ldzwtdHJpbXNucCg1LCBmaWx0ZXJlZF9zbnBmaWxlKSAjMTg3MDUgbG9jaQoKd3JpdGUuY3N2KG5ldywgIi4uL091dHB1dC9CYXlFbnYvWTIwMTdfZmlsdGVyZWRfc25wc2ZpbGUuY3N2IikKCndyaXRlLnRhYmxlKG5ld1ssMzo4XSwgIi4uL091dHB1dC9CYXlFbnYvWTIwMTdfZmlsdGVyZWRfc25wc2ZpbGVfMTg3MDUudHh0Iiwgc2VwPSJcdCIsIHJvdy5uYW1lcyA9IEYsY29sLm5hbWVzID0gRiwgcXVvdGU9RikKCgojY2hhbmdlIHRoZSBmaWx0ZXJpbmcgcHJvcG9ydGlvbiAoJSBvZiBwb3Agd2l0aCBhIG1pbm9yIGFsbGVsZSkgYW5kIHNlZSBob3cgbWFueSBzbnBzIGFyZSByZXRhaW5lZApmaWx0ZXJTbnBDb3VudDwtZnVuY3Rpb24oeCl7CiAgICBwb3BzaXplJGN1dG9mZjwtYXMuaW50ZWdlcihwb3BzaXplJEZyZXEqMip4KQogICAgcG9wbjwtcG9wc2l6ZSRjdXRvZmZbcG9wc2l6ZSRWYXIxICVpbiVwb3BzMl0KICAgIHNucGZpbGUzPC1zbnBmaWxlWyw0OjldLXBvcG4KICAgIHNucGZpbGUzPC1jYmluZChzbnBmaWxlWywyOjNdLCBzbnBmaWxlMykKICAgIGhhc19uZWdhdGl2ZXM8LWFwcGx5KHNucGZpbGUzWywzOjhdLCAxLCBmdW5jdGlvbih4KSBhbnkoeDwwKSkKICAgIHByaW50KGxlbmd0aChoYXNfbmVnYXRpdmVzW2hhc19uZWdhdGl2ZXM9PUZdKSkKfQoKIyB4IGlzIHRoZSBwcm9wb3J0aW9uIG9mIHBvcHVsYXRpb25zIHRoYXQgaGF2ZSBhIHBhcnRpY3VsYXIgc25wCmZpbHRlclNucDwtZnVuY3Rpb24oeCl7CiAgICBwb3BzaXplJGN1dG9mZjwtYXMuaW50ZWdlcihwb3BzaXplJEZyZXEqMip4KQogICAgcG9wbjwtcG9wc2l6ZSRjdXRvZmZbcG9wc2l6ZSRWYXIxICVpbiVwb3BzMl0KICAgIHNucGZpbGUzPC1zbnBmaWxlWyw0OjldLXBvcG4KICAgIHNucGZpbGUzPC1jYmluZChzbnBmaWxlWywyOjNdLCBzbnBmaWxlMykKICAgIGhhc19uZWdhdGl2ZXM8LWFwcGx5KHNucGZpbGUzWywzOjhdLCAxLCBmdW5jdGlvbih4KSBhbnkoeDwwKSkKICAgIHByaW50KGxlbmd0aChoYXNfbmVnYXRpdmVzW2hhc19uZWdhdGl2ZXM9PUZdKSkKICAgIHBvc2l0aXZlczwtd2hpY2goaGFzX25lZ2F0aXZlcz09RikKICAgIGZpbHRlcmVkPC1zbnBmaWxlM1twb3NpdGl2ZXMsXQogICAgCiAgICBmaWx0ZXJlZF9zbnBmaWxlPC1kYXRhLmZyYW1lKG49MTooMipucm93KGZpbHRlcmVkKSkpCiAgICBmb3IgKGkgaW4gMTpsZW5ndGgocG9wczIpKXsKICAgICAgICBkZjwtcmVhZC50YWJsZShwYXN0ZTAoIi4uL0RhdGEvbmV3X3ZjZi9BQy9QcnVuZWRfQUNfIixwb3BzMltpXSwiLnR4dCIpKQogICAgICAgIGNvbG5hbWVzKGRmKTwtYygiY2hyIiwicG9zIiwiQUMiLCJBTiIpCiAgICAgICAgZGY8LW1lcmdlKGZpbHRlcmVkWywxOjJdLCBkZiwgYnk9YygiY2hyIiwicG9zIikpCiAgICAgICAgI21ham9yQUMgZGF0YSBmcmFtZQogICAgICAgIG1hajwtdHJhbnNmb3JtKGRmLCBBQz1BTi1BQykKICAgICAgICBuZXdkZjwtcmJpbmQobWFqWywxOjNdLCBkZlssMTozXSkKICAgICAgICAjcmVvcmRlciB0aGUgcm93cwogICAgICAgIG48LW5yb3coZGYpCiAgICAgICAgbmV3ZGYyPC1uZXdkZltrcm9uZWNrZXIoMTpuLCBjKDAsIG4pLCAiKyIpLCBdCiAgICAgICAgbmV3ZGYyPC1uZXdkZjJbb3JkZXIobmV3ZGYyJGNocixuZXdkZjIkcG9zKSxdCiAgICAgICAgI2NvbWJpbmUgdGhlIGRhdGEKICAgICAgICBpZiAoaT09MSkgZmlsdGVyZWRfc25wZmlsZTwtY2JpbmQoZmlsdGVyZWRfc25wZmlsZSxuZXdkZjIpCiAgICAgICAgaWYgKGkhPTEpIGZpbHRlcmVkX3NucGZpbGU8LWNiaW5kKGZpbHRlcmVkX3NucGZpbGUsIG5ld2RmMlssM10pCiAgICAgICAgY29sbmFtZXMoZmlsdGVyZWRfc25wZmlsZSlbaSszXTwtcG9wczJbaV0KICAgICAgICB9CiAgICByZXR1cm4oZmlsdGVyZWRfc25wZmlsZSkKfQoKCgpuZXdzbnBzPC1maWx0ZXJTbnAoLjc1KSAgIzg3MDcgbG9jaQpuZXdzbnBzPC1uZXdzbnBzWywtMV0KIyByZS1maWx0ZXIgdG8gcmVtb3ZlIHNpdGVzIHRoYXQgaGF2ZSA8NSBhbGxlbGUgY291bnQKCm5ldzI8LXRyaW1zbnAoNSwgbmV3c25wcykgIzg2MjEgbG9jaQoKd3JpdGUudGFibGUobmV3MlssMzo4XSwgIi4uL091dHB1dC9CYXlFbnYvWTIwMTdfZmlsdGVyZWRfc25wc2ZpbGVfODYyMS50eHQiLCBzZXA9Ilx0Iiwgcm93Lm5hbWVzID0gRixjb2wubmFtZXMgPSBGLCBxdW90ZT1GKQp3cml0ZS5jc3YobmV3MiwiLi4vT3V0cHV0L0JheUVudi9ZMjAxN19maWx0ZXJlZF9zbnBzZmlsZV84NjIxLmNzdiIsIHJvdy5uYW1lcyA9IEYpCgoKYGBgCgoKIyBDcmVhdGUgZW52ZmlsZSAKCiMjIFN0YW5kYXJkaXplIHRoZSB3YXRlciB0ZW1wZXJhdHVyZSBmaWxlCmBgYHtyIGV2YWw9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CndhdGVyPC1yZWFkLmNzdigiLi4vT3V0cHV0L0JheUVudi93YXRlcnRlbXAuY3N2IikKd2F0ZXIkQzwtKHdhdGVyJG1lYW4tMzIpKjUvOQptZWFuPC1tZWFuKHdhdGVyJEMpCnNkPC1zZCh3YXRlciRDKQp3YXRlciRDX3N0ZDwtKHdhdGVyJEMtbWVhbikvc2QKd2F0ZXIyPC1kYXRhLmZyYW1lKHQod2F0ZXJbLGMoInBvcCIsIkNfc3RkIildKSkKY29sbmFtZXMod2F0ZXIyKTwtd2F0ZXIyWzEsXQp3YXRlcjI8LXdhdGVyMlssYygiQkMiLCJDQSIsIlBXUyIsIlNTIiwiVEIiLCJXQSIpXQp3cml0ZS50YWJsZSh3YXRlcjJbMixdLCAiLi4vT3V0cHV0L0JheUVudi90ZW1wX3N0ZC50eHQiLCBzZXAgPSAiXHQiLHF1b3RlID0gRiwgcm93Lm5hbWVzID0gRiwgY29sLm5hbWVzID0gRikKCiMgUHJlcGFyZSBtaW4vbWVhbi9tYXggZmlsZXMgY3JlYXRlZCBmcm9tIDIwMTcgZGF0YSAKCmVudjwtcmVhZC5jc3YoIi4uL0RhdGEvZW52X2RhdGEvc3N0XzIwMTcuY3N2IikKIyBCQz0gSGVjYXRlIFN0cmFpdCwgQkMyID0gU291dGggTW9yZXNieSAodXNlIEJDMiBmb3Igbm93KQplbnY8LWVudltlbnYkUG9wIT0iQkMiLF0KZW52JFBvcFtlbnYkUG9wPT0iQkMyIl08LSJCQyIKIyAxLiBtZWFuCm1lYW48LW1lYW4oZW52JE1lYW4pCnNkPC1zZChlbnYkTWVhbikKZW52JG1lYW5fc3RkPC0oZW52JE1lYW4tbWVhbikvc2QKCmVudjE8LWRhdGEuZnJhbWUodChlbnZbLGMoIlBvcCIsIm1lYW5fc3RkIildKSkKY29sbmFtZXMoZW52MSk8LWVudjFbMSxdCmVudjE8LWVudjFbLGMoIkJDIiwiQ0EiLCJQV1MiLCJTUyIsIlRCIiwiV0EiKV0Kd3JpdGUudGFibGUoZW52WzIsXSwgIi4uL091dHB1dC9CYXlFbnYvc3N0MjAxN19tZWFuX3N0ZC50eHQiLCBzZXAgPSAiXHQiLHF1b3RlID0gRiwgcm93Lm5hbWVzID0gRiwgY29sLm5hbWVzID0gRikKCiMyLiBtaW4KbWVhbjwtbWVhbihlbnYkTWluKQpzZDwtc2QoZW52JE1pbikKZW52JG1pbl9zdGQ8LShlbnYkTWluLW1lYW4pL3NkCmVudjE8LWRhdGEuZnJhbWUodChlbnZbLGMoIlBvcCIsIm1pbl9zdGQiKV0pKQpjb2xuYW1lcyhlbnYxKTwtZW52MVsxLF0KZW52MTwtZW52MVssYygiQkMiLCJDQSIsIlBXUyIsIlNTIiwiVEIiLCJXQSIpXQp3cml0ZS50YWJsZShlbnZbMixdLCAiLi4vT3V0cHV0L0JheUVudi9zc3QyMDE3X21pbl9zdGQudHh0Iiwgc2VwID0gIlx0IixxdW90ZSA9IEYsIHJvdy5uYW1lcyA9IEYsIGNvbC5uYW1lcyA9IEYpCgojTWF4Cm1lYW48LW1lYW4oZW52JE1heCkKc2Q8LXNkKGVudiRNYXgpCmVudiRtYXhfc3RkPC0oZW52JE1heC1tZWFuKS9zZAoKZW52MTwtZGF0YS5mcmFtZSh0KGVudlssYygiUG9wIiwibWF4X3N0ZCIpXSkpCmNvbG5hbWVzKGVudjEpPC1lbnYxWzEsXQplbnYxPC1lbnYxWyxjKCJCQyIsIkNBIiwiUFdTIiwiU1MiLCJUQiIsIldBIildCndyaXRlLnRhYmxlKGVudlsyLF0sICIuLi9PdXRwdXQvQmF5RW52L3NzdDIwMTdfbWF4X3N0ZC50eHQiLCBzZXAgPSAiXHQiLHF1b3RlID0gRiwgcm93Lm5hbWVzID0gRiwgY29sLm5hbWVzID0gRikKCmBgYAoKIyMgQWRkIGxhdGl0dWRlIGluZm9ybWFpdG9uICAKYGBge3IgZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCiNhZGQgbGF0aXR1ZGU/CmxhdDwtYyg1Mi4xLDM3LjY5LDYwLjU0LCA1OS45MywgNTguOCwgNDcuNikKI3N0YW5kYXJkaXplCmxhdDI8LShsYXQtbWVhbihsYXQpKS9zZChsYXQpCndhdGVyMlszLF08LWxhdDIKd3JpdGUudGFibGUod2F0ZXIyWzI6MyxdLCAiLi4vT3V0cHV0L0JheUVudi90ZW1wX2xhdF9zdGQudHh0Iiwgc2VwID0gIlx0IixxdW90ZSA9IEYsIHJvdy5uYW1lcyA9IEYsIGNvbC5uYW1lcyA9IEYpCgp0PC11bmxpc3QoYXMudmVjdG9yKHdhdGVyMlsyLF0pKQp0PC1hcy5udW1lcmljKHQpCmNvci50ZXN0KGxhdCwgdCwgbWV0aG9kPSJwZWFyc29uIikKI3QgPSAtMS45MzYzLCBkZiA9IDQsIHAtdmFsdWUgPSAwLjEyNDkKI2xhdGl0dWRlIGFuZCB0ZW1wZXJhdHVyZSBkbyBub3Qgc2hvdyBjb3JyZWxhdGlvbgpgYGAKCgojIFJ1biBCYXlFbnYyIG9uIEZhcm0gdG8gb2J0YWluIGEgQ292YXJpYWNuZSBNYXRyaXgKCiMjIENyZWF0ZSBzbHVybSBzY3JpcHRzIHRvIGVzdGlhbXRlIHRoZSBjb3ZhcmlhY25lIG1hdHJpeCAgCiMjIyAiUnVuIGFzICdQb29sZWQgTkdTIGRhdGEnCmBgYHtyIGV2YWw9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgpzaW5rKHBhc3RlMCgiLi4vRGF0YS9TbHVybXNjcmlwdHMvYmF5ZW52X3J1bi5zaCIpKQpjYXQoIiMhL2Jpbi9iYXNoIC1sXG4iKQpjYXQocGFzdGUwKCIjU0JBVENIIC0tam9iLW5hbWU9YmF5ZW52IFxuIikpCmNhdChwYXN0ZTAoIiNTQkFUQ0ggLS1tZW09MTZHIFxuIikpIApjYXQocGFzdGUwKCIjU0JBVENIIC0tbnRhc2tzPTggXG4iKSkgCmNhdChwYXN0ZTAoIiNTQkFUQ0ggLWUgPWJheWVudi5lcnIgIFxuIikpCmNhdChwYXN0ZTAoIiNTQkFUQ0ggLS10aW1lPTcyOjAwOjAwICBcbiIpKQpjYXQocGFzdGUwKCIjU0JBVENIIC1wIGhpZ2ggIFxuIikpCmNhdCgiXG5cbiIpCmNhdCgibW9kdWxlIGxvYWQgYmF5ZW52MiBcblxuIikgCiAgCmNhdCgiYmF5ZW52MiAtaSAva3Rpc3QvcGgvZGF0YS9iYXllbnYvWTIwMTdfZmlsdGVyZWRfc25wc2ZpbGVfODYyMS50eHQgLXMgc2FtcGxlc2l6ZS50eHQgLXAgNiAtayAxMDAwMDAgLXIgMTIzNDUgLXggID4gWTIwMTdfbWF0cml4My5vdXQgXG4iKQpzaW5rKE5VTEwpCgojZXh0cmFjdCB0aGUgbGFzdCBjb3ZhcmlhbmNlIG1hdHJpeCBhcyBZMjAxN21hdHJpeC50eHQKYGBgCgojIyMgVmlzdWFsaXplIHRoZSBjb3JyZWxhdGlvbiBtYXRyaXggZnJvbSBCYXlFbnYyCiogUnVuIHdpdGggJ1Bvb2xlZCBOR1MnIG1ldGhvZAoKYGBge3IgZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KY292ID0gYXMubWF0cml4KHJlYWQudGFibGUoZmlsZT0iLi4vT3V0cHV0L0JheUVudi9ZMjAxN19tYXRyaXgudHh0IixoZWFkZXI9RikpCm1hdCA9IGNvdjJjb3IoY292KQoKcm93Lm5hbWVzKG1hdCk8LXBvcHMyCmNvbG5hbWVzKG1hdCk8LXBvcHMyCntwZGYoIi4uL091dHB1dC9CYXlFbnYvQ29ycGxvdF95MjAxN184NjIxc25wc19ydW4xLnBkZiIsIHdpZHRoID0gNSwgaGVpZ2h0PTUpCmNvcnJwbG90KG1hdCwgaXMuY29ycj1UKQpkZXYub2ZmKCl9CgptYXQ8LW1hdFssYygiVEIxNyIsIlBXUzE3IiwiU1MxNyIsIkJDMTciLCJXQTE3IiwiQ0ExNyIpXQptYXQ8LW1hdFtjKCJUQjE3IiwiUFdTMTciLCJTUzE3IiwiQkMxNyIsIldBMTciLCJDQTE3IiksXQoKbWF0MjwtbWF0CmRpYWcobWF0Mik8LU5BCm1hdDJbdXBwZXIudHJpKG1hdDIpXTwtTkEKbWF0Mm08LW1lbHQobWF0MikKCm1hdDJtJFZhcjE8LWZhY3RvcihtYXQybSRWYXIxLCBsZXZlbHM9YygiVEIxNyIsIlBXUzE3IiwiU1MxNyIsIkJDMTciLCJXQTE3IiwiQ0ExNyIpKQptYXQybSRWYXIyPC1mYWN0b3IobWF0Mm0kVmFyMiwgbGV2ZWxzPWMoIlRCMTciLCJQV1MxNyIsIlNTMTciLCJCQzE3IiwiV0ExNyIsIkNBMTciKSkKCm1hdDJtJGNvbG9yPC0iYSIKbWF0Mm0kY29sb3JbbWF0Mm0kdmFsdWU+MC4wMV08LSJiIgpnZ3Bsb3QobWF0Mm0sIGFlcyh4PVZhcjEsIHk9VmFyMiwgZmlsbD12YWx1ZSkpKwogICAgZ2VvbV90aWxlKGNvbG9yID0gIndoaXRlIikrCiAgICBzY2FsZV9maWxsX2dyYWRpZW50bihjb2xvcnM9YygiIzA3MzQ2NiIsIndoaXRlIiksIGxpbWl0cz1jKG1pbihtYXQybSR2YWx1ZSwgbmEucm09VCksIDAuMDQpLCBuYS52YWx1ZT0iZ3JheTkwIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lPSJDb3JyZWxhdGlvbiIpKwogICAgdGhlbWVfbWluaW1hbCgpKyB4bGFiKCIiKSt5bGFiKCIiKSsKICAgIGdlb21fdGV4dChhZXMoVmFyMSwgVmFyMiwgbGFiZWwgPSByb3VuZCh2YWx1ZSwgZGlnaXRzID0gMyksIGNvbG9yPWNvbG9yKSwgIHNpemUgPSA1KSsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YyggIndoaXRlIiwiYmxhY2siKSwgZ3VpZGU9J25vbmUnKQoKZ2dzYXZlKCIuLi9PdXRwdXQvQmF5RW52L0NvcnJlbGF0aW9uX3Bsb3RfODYyMXNucHMucG5nIiwgd2lkdGggPSA2LCBoZWlnaHQgPSA0LjUsIGRwaT0zMDApICAgIAoKcGFsZXR0ZTEgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCJibHVlIiwid2hpdGUiLCJyZWQiKSkobiA9IDI5OSkKCm1hdDM8LW1hdApkaWFnKG1hdDMpPC1OQQp7cGRmKCIuLi9PdXRwdXQvQmF5RW52L0hlYXRtYXBfeTIwMTdfODYyMXNucHNfcnVuMS5wZGYiLCB3aWR0aCA9NSwgaGVpZ2h0PTMpCmhlYXRtYXAuMihtYXQzLGNvbD1wYWxldHRlMSxzeW1tPVQsc3lta2V5PVQsZGVuZHJvZ3JhbT0icm93Iix0cmFjZT0ibm9uZSIsQ29sdj0iUm93diIsLGJyZWFrcyA9IHNlcSgtMC4xLCAwLjEsIGxlbmd0aC5vdXQgPSAzMDApLGxhYkNvbD0iIiAsa2V5PUYsY2V4Um93PTEpCmRldi5vZmYoKX0KCiNDb3JyZWxhdGlvbnMgYXJlIGRpZmZlcmVudCBmcm9tIEZzdC4KYGBgCiFbXSguLi9PdXRwdXQvQmF5RW52L0NvcnBsb3RfeTIwMTdfODYyMXNucHNfcnVuMS5wbmcpe3dpZHRoPTYwJX0KCgohW10oLi4vT3V0cHV0L0JheUVudi9Db3JyZWxhdGlvbl9wbG90Xzg2MjFzbnBzLnBuZyl7d2lkdGg9NjAlfQpGc3QgcGxvdAohW10oLi4vT3V0cHV0L1NGUy9Gc3RfbWF0cml4XzIwMTdfYWxsLnBuZyl7d2lkdGg9NjAlfSAgCgoKCiFbXSguLi9PdXRwdXQvQmF5RW52L0hlYXRtYXBfeTIwMTdfODYyMXNucHNfcnVuMS5wbmcpe3dpZHRoPTcwJX0KCiogQ29ycmVsYXRpb25zIGFyZSBzbGlnaHRseSBkaWZmZXJlbnQgZnJvbSB0aGUgcmVsYXRpb25zaGlwcyBmcm9tIHRoZSBGc3QgdmFsdWVzLgoKCgo8YnI+Cjxicj4KCiMgUnVuIEJheUVudjIgd2l0aCB0aGUgbWF0cml4IHByb2R1Y2VkIGluIHRoZSBzdGVwMSB3aXRoIHRoZSB0ZW1wZXJhdHVyZSBkYXRhCgpgYGB7ciBldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKIyBSdW4gQmF5RW52MiBmb3IgYWxsIFNOUFMgd2l0aCBlbnZpcm9ubWVudGFsIGRhdGEKc2luayhwYXN0ZTAoIi4uL0RhdGEvU2x1cm1zY3JpcHRzL2JheWVudl9ydW4yLnNoIikpCmNhdCgiIyEvYmluL2Jhc2ggLWxcbiIpCmNhdChwYXN0ZTAoIiNTQkFUQ0ggLS1qb2ItbmFtZT1iYXllbnYyIFxuIikpCmNhdChwYXN0ZTAoIiNTQkFUQ0ggLS1tZW09MTZHIFxuIikpIApjYXQocGFzdGUwKCIjU0JBVENIIC0tbnRhc2tzPTggXG4iKSkgCmNhdChwYXN0ZTAoIiNTQkFUQ0ggLWUgPWJheWVudjIuZXJyICBcbiIpKQpjYXQocGFzdGUwKCIjU0JBVENIIC0tdGltZT03MjowMDowMCAgXG4iKSkKY2F0KHBhc3RlMCgiI1NCQVRDSCAtcCBoaWdoICBcbiIpKQpjYXQoIlxuXG4iKQpjYXQoIm1vZHVsZSBsb2FkIGJheWVudjIgXG5cbiIpIAoKI0ZpcnN0IHNwbGl0IHRoZSBTTlBmaWxlcyBiYXNlZCBvbiBjYWxjX2Jmcy5zaCAKY2F0KCJzcGxpdCAtYSAxMCAtbCAyIFkyMDE3X2ZpbHRlcmVkX3NucHNmaWxlXzg2MjEudHh0IHNucF9iYXRjaCBcblxuIikKCiNSdW4gYmF5ZW52MiBmb3IgZWFjaCBzbnAKY2F0KCJmb3IgZiBpbiAkKGxzIHNucF9iYXRjaCpcbiIpCmNhdCgiZG9cbiIpCmNhdCgiYmF5ZW52MiAtaSAkZiAtZSB0ZW1wX3N0ZC50eHQgLW0gWTIwMTdfbWF0cml4LnR4dCAtayAxMDAwMDAgLXIgMTIzNDUgLXAgNiAtbiAxIC10IC1jIFxuIikKY2F0KCJkb25lIFxuIikKc2luayhOVUxMKQpgYGAKCgojIyBSZXN1bHRzIGZyb20gQmF5RW52MiBSdW4xIAoKYGBge3IgZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCmJmPC1yZWFkLnRhYmxlKCIuLi9PdXRwdXQvQmF5RW52L1JVTjFfYmZfZW52aXJvbi50ZW1wX3N0ZC50eHQiLCBoZWFkZXIgPSBGKQpjb2xuYW1lcyhiZik8LWMoImJhdGNoX2lkIiwgIkJGIiwicmhvIiwiciIpCgojIExpc3QgYWxsIHNwbGl0IGZpbGUgbmFtZXM6CiMgbHMgc25wX2JhdGNoKiA+c25wX2ZpbGVuYW1lcy50eHQKCm5hbWVzPC1yZWFkLnRhYmxlKCIuLi9PdXRwdXQvQmF5RW52L3NucF9maWxlbmFtZXMudHh0IikKCiNzbnAgbmFtZXMKc25wczwtcmVhZC5jc3YoIi4uL091dHB1dC9CYXlFbnYvWTIwMTdfZmlsdGVyZWRfc25wc2ZpbGVfODYyMS5jc3YiKQpzbnBzX25hbWU8LXNucHNbc2VxKDEsbnJvdyhzbnBzKSxieT0yKSxjKCJjaHIiLCJwb3MiKV0Kc25wc19uYW1lJGJhdGNoX2lkPC1uYW1lcyRWMQoKIyBub3cgbWVyZ2UgdGhlIHR3byB0YWJsZXMKYmY8LW1lcmdlKHNucHNfbmFtZSwgYmYsIGJ5PSJiYXRjaF9pZCIpCmJmJGNocm9tPC1hcy5pbnRlZ2VyKGdzdWIoImNociIsJycsYmYkY2hyKSkKYmY8LWJmW29yZGVyKGJmJGNocm9tLCBiZiRwb3MpLF0KYmYkbjwtMTpucm93KGJmKQpldmVuczwtcGFzdGUwKCJjaHIiLHNlcSgyLDI2LCBieT0yKSkKYmYkY29sb3I8LSJhIgpiZiRjb2xvcltiZiRjaHIgJWluJSBldmVuc108LSJiIgpnZ3Bsb3QoYmYsIGFlcyh4PW4seT1CRiwgY29sb3I9Y29sb3IpKSt4bGFiKCcnKSsKICAgIGdlb21fcG9pbnQoc2l6ZT0wLjUsIGFscGhhPTAuNikreWxhYigiQmF5ZXMgZmFjYXRvciIpKwogICAgdGhlbWVfY2xhc3NpYygpKwogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJncmF5NzAiLCJzdGVlbGJsdWUiKSwgZ3VpZGU9Im5vbmUiKSsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKQpnZ3NhdmUoIi4uL091dHB1dC9iYXlzRmFjdG9yX2Fjcm9zc0dlbm9tZV9ZMjAxN19ydW4xLnBuZyIsIHdpZHRoID0gNywgaGVpZ2h0ID0gMywgZHBpPTMwMCkKYGBgCiFbXSguLi9PdXRwdXQvYmF5c0ZhY3Rvcl9hY3Jvc3NHZW5vbWVfWTIwMTdfcnVuMS5wbmcpCgoKKiBSZW1vdmUgdGhlIDIgb3V0bGllcnMgJiBwbG90IGluIGxvZyBzY2FsZSAKYGBge3IgZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCiNyZW1vdmUgdGhlIDIgb3V0bGllcnMKZ2dwbG90KGJmLCBhZXMoeD1uLHk9QkYsIGNvbG9yPWNvbG9yKSkreGxhYignJykrCiAgICBnZW9tX3BvaW50KHNpemU9MC41LCBhbHBoYT0wLjYpK3lsYWIoIkJheWVzIGZhY2F0b3IiKSsKICAgIHRoZW1lX2NsYXNzaWMoKSsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiZ3JheTcwIiwic3RlZWxibHVlIiksIGd1aWRlPSJub25lIikrCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcy54PWVsZW1lbnRfYmxhbmsoKSkrCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gbGFiZWxfY29tbWEoKSwgdHJhbnMgPSAnbG9nMTAnLCBsaW1pdHM9YygwLjA1LDEwMDAwKSkKZ2dzYXZlKCIuLi9PdXRwdXQvYmF5c0ZhY3Rvcl9hY3Jvc3NHZW5vbWVfWTIwMTdfcnVuMV9sb2dzY2FsZWQucG5nIiwgd2lkdGggPSA3LCBoZWlnaHQgPSAzLCBkcGk9MzAwKQoKI3JobyB2YWx1ZXMKZ2dwbG90KGJmLCBhZXMoeD1uLHk9cmhvLCBjb2xvcj1jb2xvcikpK3hsYWIoJycpKwogICAgZ2VvbV9wb2ludChzaXplPTAuNSwgYWxwaGE9MC42KSt5bGFiKCJTcGVhZXJtYW4ncyByaG8iKSsKICAgIHRoZW1lX2NsYXNzaWMoKSsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiZ3JheTcwIiwic3RlZWxibHVlIiksIGd1aWRlPSJub25lIikrCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcy54PWVsZW1lbnRfYmxhbmsoKSkKZ2dzYXZlKCIuLi9PdXRwdXQvUmhvX2Fjcm9zc0dlbm9tZV9ZMjAxN19ydW4xLnBuZyIsIHdpZHRoID0gNywgaGVpZ2h0ID0gMywgZHBpPTMwMCkKCmBgYAoKIVtdKC4uL091dHB1dC9iYXlzRmFjdG9yX2Fjcm9zc0dlbm9tZV9ZMjAxN19ydW4xX2xvZ3NjYWxlZC5wbmcpCgojIyBTcGVhcm1hbidzIHJobyB2YWx1ZXMKCiFbXSguLi9PdXRwdXQvUmhvX2Fjcm9zc0dlbm9tZV9ZMjAxN19ydW4xLnBuZykKCiMjIEZpbmQgaGlnaGx5IHJhbmtlZCBzbnBzIGluIEJGIGFuZCDPgQoKYGBge3IgZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCiN0b3AgMSUgb2YgQkYKYmY8LWJmW29yZGVyKGJmJEJGLCBkZWNyZWFzaW5nID0gVCksXQpuPC1hcy5pbnRlZ2VyKG5yb3coYmYpKjAuMDEpCnRvcDFiZjwtYmZbMTpuLCBdCgptaW4odG9wMWJmJEJGKQojNTQuOTQgaXMgdGhlIGxvd2VzdCA9IGN1dG9mZiB2YWx1ZXMKCmhpc3QoYmYkQkZbYmYkQkY8MTAwXSkKI3Byb3BvcnRpb24gb2YgQkY+MjAKbGVuZ3RoKGJmJEJGW2JmJEJGPj0yMF0pL25yb3coYmYpCiMwLjAxNjAwNzQyICAgMS42JSBvZiBCRnMgYXJlIG92ZXIgMjAKCiN0b3AgMSUgb2YgcmhvCmJmPC1iZltvcmRlcihhYnMoYmYkcmhvKSwgZGVjcmVhc2luZyA9IFQpLF0KI248LWFzLmludGVnZXIobnJvdyhiZikqMC4wMSkKdG9wMXJobzwtYmZbMTpuLCBdCgptaW4oYWJzKHRvcDFyaG8kcmhvKSkKIzAuNTMwODMgIHJobyB2YWx1ZSBjdXRvZmYgPSAwLjUzMDgzCgpoaXN0KGFicyhiZiRyaG8pKQojIE1hbnVhbCBzYXlzIHggKHRvcCB4JSBCRikgPCB5ICh0b3AgeSUgcmhvKSAKCiNpbmNyZWFzZSB0aGUgY3V0b2ZmIHZhbHVlIHRvIDYwPwpsZW5ndGgoYmYkQkZbYmYkQkY+PTYwXSkvbnJvdyhiZikgCiMgMC45NjMlCnRvcDFiZjwtdG9wMWJmW3RvcDFiZiRCRj49NjAsXQoKb3V0bGllcnM8LWludGVyc2VjdCh0b3AxYmYkYmF0Y2hfaWQsIHRvcDFyaG8kYmF0Y2hfaWQpCiMyOSBjYW5kaWRhdGUgbG9jaSAKCm91dHM8LWJmW2JmJGJhdGNoX2lkICVpbiUgb3V0bGllcnMsXQoKIyBwbG90IGFjcm9zcyB0aGUgZ2Vub21lIHRvIHNlZSB0aGUgbG9jYXRpb25zIG9mIG91dGxpZXJzCm91dHM8LW91dHNbb3JkZXIob3V0cyRuKSxdCgojbm8gY2hyb21vc29tZSAyNApjaHJvbXM8LWMoMToyMywgMjU6MjYpCnBvc3M8LWRhdGEuZnJhbWUoY2hyPXBhc3RlMCgiY2hyIixjaHJvbXMpKQprPTEKaT0xCmZvciAoaiBpbiBjaHJvbXMpewogICAgICAgIGRmPC1iZltiZiRjaHI9PXBhc3RlMCgiY2hyIixqKSxdCiAgICAgICAgcG9zcyRzdGFydFtpXTwtawogICAgICAgIHBvc3MkZW5kW2ldPC1rK25yb3coZGYpLTEKICAgICAgICBrPWsrbnJvdyhkZikKICAgICAgICBpPWkrMQp9CnBvc3MkeDwtcG9zcyRzdGFydCsocG9zcyRlbmQtcG9zcyRzdGFydCkvMgoKYmYkb3V0bGllcjwtIm4iCmJmJG91dGxpZXJbYmYkY29sb3I9PSJiIl08LSJvIgpiZiRvdXRsaWVyW2JmJGJhdGNoX2lkICVpbiVvdXRsaWVyc108LSJ5IgoKZ2dwbG90KGJmLCBhZXMoeD1uLCB5PUJGLCBjb2xvcj1vdXRsaWVyKSkrCiAgICBnZW9tX3BvaW50KHNpemU9MC42KSsKICAgIHNjYWxlX3lfY29udGludW91cyh0cmFucz0nbG9nMTAnLGxhYmVsPWxhYmVsX2NvbW1hKCkpKwogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCIjQzBDMEMwODgiLCIjQUREOEU2ODAiLCIjRkYzMjkzQjMiKSwgZ3VpZGU9Im5vbmUiKSsKICAgIHNjYWxlX3hfY29udGludW91cyhuYW1lPSJDaHJvbW9zb21lIiwgYnJlYWtzPXBvc3MkeCwgbGFiZWxzPWNocm9tcykrCiAgICB0aGVtZV9jbGFzc2ljKCkrCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSA2MCwgY29sb3I9ImRhcmtyZWQiLCBzaXplPTAuMywgbGluZXR5cGU9MikKCmdnc2F2ZSgiLi4vT3V0cHV0L0JheUVudi9SdW4xX0JGX3Job19jb21iaW5lZF9vdXRsaWVyc184NjIxc25wcy5wbmciLCB3aWR0aCA9IDcsIGhlaWdodD0zLjUsIGRwaT0zMDApCgpgYGAKCiFbXSguLi9PdXRwdXQvQmF5RW52L1J1bjFfQkZfcmhvX2NvbWJpbmVkX291dGxpZXJzXzg2MjFzbnBzLnBuZykKCmBgYHtyIGV2YWw9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiNmaW5kIGFubm90YXRpb25zIGZvciB0aGUgb3V0bGllciBsb2NpCgpjaHJzPC11bmlxdWUob3V0cyRjaHIpCmdlbmVzPC1kYXRhLmZyYW1lKCkKYW5ub3RhdGlvbnM8LWxpc3QoKQprPTEKdD0xCmZvciAoaSBpbiAxOmxlbmd0aChjaHJzKSl7CiAgICBkZjwtb3V0c1tvdXRzJGNocj09Y2hyc1tpXSxdCiAgICBjaDwtYXMuaW50ZWdlcihnc3ViKCJjaHIiLCcnLGNocnNbaV0pKQogICAgYW5ubzwtcmVhZC50YWJsZShwYXN0ZTAoIi4uL0RhdGEvYW5ub3RhdGlvbnMvYW5ub3RhdGlvbl9ieUNocm9tb3NvbWUvIiwgY2gpLCBzZXA9Ilx0IikKICAgIGNvbG5hbWVzKGFubm8pWzQ6NV08LWMoInN0YXJ0IiwiZW5kIikKICAgIAogICAgZm9yIChqIGluIDE6bnJvdyhkZikpewogICAgICAgIGFubm90PC1hbm5vW2Fubm8kc3RhcnQ8PWRmJHBvc1tqXSAmIGFubm8kZW5kPmRmJHBvc1tqXSxdCiAgICAgICAgaWYobnJvdyhhbm5vdCkhPTApewogICAgICAgICAgICAgZ2VuZTwtYW5ub3RbYW5ub3QkVjM9PSJnZW5lIixdCiAgICAgICAgICAgIGlmIChucm93KGdlbmUpPjApIGdlbmVzPC1yYmluZChnZW5lcywgZ2VuZSk7IGs9aysxCiAgICAgICAgICAgIGlmIChucm93KGdlbmUpPT0wKSBhbm5vdGF0aW9uc1tbdF1dPC1hbm5vdDsgdD10KzEKICAgICAgICB9CiAgICB9Cn0KCgpnZW5lX2xpc3Q8LWdlbmVzWywxOjVdCgpmb3IgKGkgaW4gMTogbnJvdyhnZW5lcykpewogICAgaW5mbzwtdW5saXN0KHN0cnNwbGl0KGdlbmVzJFY5W2ldLCAiXFw7IikpCiAgICBpbmZvPC1zdHJfdHJpbShpbmZvLCBzaWRlPSJsZWZ0IikKICAgIGlkPC1pbmZvW2dyZXAoImdlbmVfaWQiLCBpbmZvKV0KICAgIGdlbmVfbGlzdCRnZW5lX2lkW2ldPC1nc3ViKCJnZW5lX2lkICIsJycsIGlkKQogICAgCiAgICBuYW1lPC1pbmZvW2dyZXAoImdlbmVfbmFtZSIsIGluZm8pXQogICAgaWYgKGxlbmd0aChuYW1lKSE9MCkgZ2VuZV9saXN0JGdlbmVfbmFtZVtpXTwtZ3N1YigiZ2VuZV9uYW1lICIsJycsIG5hbWUpCiAgICBpZiAobGVuZ3RoKG5hbWUpPT0wKSBnZW5lX2xpc3QkZ2VuZV9uYW1lW2ldPC0nJwogICAgCiAgICB0eXBlPC1pbmZvW2dyZXAoImdlbmVfYmlvdHlwZSIsIGluZm8pXQogICAgZ2VuZV9saXN0JGdlbmVfdHlwZVtpXTwtZ3N1YigiZ2VuZV9iaW90eXBlICIsJycsIHR5cGUpCn0gICAgIAoKd3JpdGUuY3N2KGdlbmVfbGlzdCwgIi4uL091dHB1dC9CYXlFbnYvUnVuMV9PdXRsaWVyc19nZW5lX2luZm8uY3N2IikKc2luaygiLi4vT3V0cHV0L0JheUVudi9nZW5lcy50eHQiKQpjYXQocGFzdGUoZ2VuZV9saXN0JGdlbmVfaWQpLHNlcD0iOyIpCnNpbmsoTlVMTCkKIyBlbnRlciB0aGUgZ2VuZXMgdG8gU2hpbnlHbwoKIyBBbHNvIGxvb2sgYXQgdGhlIHJlZ2lvbnMgc3Vycm91bmRpbmcgdGhlIGxvY2kgKHRoaXMgaXMgbW9yZSBzZW5zaWJsZSBhcHByb2FjaCBhcyBhbGwgbGlua2VkIGxvY2kgYXJlIHJlbW92ZWQpCgojQ3JlYXRlIGEgYmVkIGZpbGUgY29udGFpbmluZyByZWdpb25zIG5lYXIgb3V0bGllciBsb2NpCgpvdXRfYmVkMTwtb3V0c1ssYygiY2hyIiwicG9zIildCm91dF9iZWQxJHN0YXJ0PC1vdXRzJHBvcy0yMDAwMDAKb3V0X2JlZDEkZW5kPC1vdXRzJHBvcysyMDAwMDAKd3JpdGUudGFibGUob3V0X2JlZDFbLGMoMSwzLDQpXSwgcGFzdGUwKCIuLi9PdXRwdXQvQmF5RW52L1J1bjFfb3V0bGllcl9iZWRfMjAwa2J1ZmZlci5iZWQiKSxxdW90ZSA9IEYsIHJvdy5uYW1lcyA9IEYsIGNvbC5uYW1lcyA9IFQsc2VwID0gIlx0IikKCm91dF9iZWQyPC1vdXRzWyxjKCJjaHIiLCJwb3MiKV0Kb3V0X2JlZDIkc3RhcnQ8LW91dHMkcG9zLTEwMDAwMApvdXRfYmVkMiRlbmQ8LW91dHMkcG9zKzEwMDAwMAp3cml0ZS50YWJsZShvdXRfYmVkMlssYygxLDMsNCldLCBwYXN0ZTAoIi4uL091dHB1dC9CYXlFbnYvUnVuMV9vdXRsaWVyX2JlZF8xMDBrYnVmZmVyLmJlZCIpLHF1b3RlID0gRiwgcm93Lm5hbWVzID0gRiwgY29sLm5hbWVzID0gVCxzZXAgPSAiXHQiKQoKI2NyZWF0ZSBhIG5ldyB2Y2Ygd2l0aCB0aGUgYmVkIGZpbGVzIApiZWRmaWxlczwtbGlzdC5maWxlcygiLi4vT3V0cHV0L0JheUVudiIsIHBhdHRlcm49Ii5iZWQiKQpzaW5rKCIuLi9PdXRwdXQvQmF5RW52L2NlYXRlVkNGc19iZWQxLnNoIikKY2F0KCIjIS9iaW4vYmFzaCBcblxuIikKZm9yIChpIGluIDE6bGVuZ3RoKGJlZGZpbGVzKSl7CiAgICBmbmFtZTwtZ3N1YigiLmJlZCIsJycsIGJlZGZpbGVzW2ldKQogICAgY2F0KHBhc3RlMCgidmNmdG9vbHMgLS1nenZjZiBEYXRhL25ld192Y2YvUEhfRFA2MDBfNzAwMF9taW5RMjBfbWluTVEzMF9OUzAuNV9tYWYwNS52Y2YuZ3ogLS1iZWQgT3V0cHV0L0JheUVudi8iLCBiZWRmaWxlc1tpXSwgIiAtLW91dCBPdXRwdXQvQmF5RW52LyIsIGZuYW1lLCIgLS1yZWNvZGUgLS1rZWVwLUlORk8tYWxsIFxuIikpCn0Kc2luayhOVUxMKSAKCiNjcmVhdGUgYSBiYXNoIHNjcmlwdCB0byBydW4gc25wRWZmCnZmaWxlczwtbGlzdC5maWxlcygiLi4vT3V0cHV0L0JheUVudi8iLCBwYXR0ZXJuPSIucmVjb2RlLnZjZiIpCgpzaW5rKCJ+L3Byb2dyYW1zL3NucEVmZi9ydW5zbnBFZmZfYmF5ZW52X291dGxpZXJzMS5zaCIpCmNhdCgiIyEvYmluL2Jhc2ggXG5cbiIpCmZvciAoaSBpbiAxOmxlbmd0aCh2ZmlsZXMpKXsKICAgIGZuYW1lPC1nc3ViKCIucmVjb2RlLnZjZiIsIiIsdmZpbGVzW2ldKQogICAgY2F0KHBhc3RlMCgiamF2YSAtWG14OGcgLWphciBzbnBFZmYuamFyIENoX3YyLjAuMi45OSB+L1Byb2plY3RzL1BhY0hlcnJpbmcvT3V0cHV0L0JheUVudi8iLHZmaWxlc1tpXSwgIiAtc3RhdHMgfi9Qcm9qZWN0cy9QYWNIZXJyaW5nL091dHB1dC9CYXlFbnYvT3V0bGllcl9nZW5lcy8iLGZuYW1lLCIuaHRtbCA+ICB+L1Byb2plY3RzL1BhY0hlcnJpbmcvT3V0cHV0L0JheUVudi9PdXRsaWVyX2dlbmVzL0Fubm8uIixmbmFtZSwiLnZjZiBcbiIpKQogICAgCiAgICAjZXh0cmFjdCB0aGUgYW5ub3RhdGlvbiBpbmZvcm1hdGlvbgogICAgY2F0KHBhc3RlMCgiYmNmdG9vbHMgcXVlcnkgLWYgJyVDSFJPTSAlUE9TICVJTkZPL0FGICVJTkZPL0FOTlxcbicgfi9Qcm9qZWN0cy9QYWNIZXJyaW5nL091dHB1dC9CYXlFbnYvT3V0bGllcl9nZW5lcy9Bbm5vLiIsZm5hbWUsIi52Y2YgPiB+L1Byb2plY3RzL1BhY0hlcnJpbmcvT3V0cHV0L0JheUVudi9PdXRsaWVyX2dlbmVzLyIsZm5hbWUsIl9hbm5vdGF0aW9uIFxuXG4iKSkKCn0Kc2luayhOVUxMKSAgCgpgYGAKCgojIEEgY292YXJpYWNuZSBtYXRyaXggd2l0aCAxODcwNSBTTlBzICAKCiogUnVuIHdpdGggIlBvb2xlZCBOR1MiIG1ldGhvZCAgCmBgYHtyIGV2YWw9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmNvdiA9IGFzLm1hdHJpeChyZWFkLnRhYmxlKGZpbGU9Ii4uL091dHB1dC9CYXlFbnYvWTIwMTdfbWF0cml4XzE4ay50eHQiLGhlYWRlcj1GKSkKbWF0ID0gY292MmNvcihjb3YpCnJvdy5uYW1lcyhtYXQpPC1wb3BzMgpjb2xuYW1lcyhtYXQpPC1wb3BzMgptYXQ8LW1hdFssYygiVEIxNyIsIlBXUzE3IiwiU1MxNyIsIkJDMTciLCJXQTE3IiwiQ0ExNyIpXQptYXQ8LW1hdFtjKCJUQjE3IiwiUFdTMTciLCJTUzE3IiwiQkMxNyIsIldBMTciLCJDQTE3IiksXQoKbWF0MjwtbWF0CmRpYWcobWF0Mik8LU5BCm1hdDJbdXBwZXIudHJpKG1hdDIpXTwtTkEKbWF0Mm08LW1lbHQobWF0MikKCm1hdDJtJFZhcjE8LWZhY3RvcihtYXQybSRWYXIxLCBsZXZlbHM9YygiVEIxNyIsIlBXUzE3IiwiU1MxNyIsIkJDMTciLCJXQTE3IiwiQ0ExNyIpKQptYXQybSRWYXIyPC1mYWN0b3IobWF0Mm0kVmFyMiwgbGV2ZWxzPWMoIlRCMTciLCJQV1MxNyIsIlNTMTciLCJCQzE3IiwiV0ExNyIsIkNBMTciKSkKCm1hdDJtJGNvbG9yPC0iYSIKbWF0Mm0kY29sb3JbbWF0Mm0kdmFsdWU+MC4wMDhdPC0iYiIKZ2dwbG90KG1hdDJtLCBhZXMoeD1WYXIxLCB5PVZhcjIsIGZpbGw9dmFsdWUpKSsKICAgIGdlb21fdGlsZShjb2xvciA9ICJ3aGl0ZSIpKwogICAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3JzPWMoIiMwNzM0NjYiLCJ3aGl0ZSIpLCBsaW1pdHM9YyhtaW4obWF0Mm0kdmFsdWUsIG5hLnJtPVQpLCBtYXgobWF0Mm0kdmFsdWUsIG5hLnJtPVQpKzAuMDEpLCBuYS52YWx1ZT0iZ3JheTkwIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lPSJDb3JyZWxhdGlvbiIpKwogICAgdGhlbWVfbWluaW1hbCgpKyB4bGFiKCIiKSt5bGFiKCIiKSsKICAgIGdlb21fdGV4dChhZXMoVmFyMSwgVmFyMiwgbGFiZWwgPSByb3VuZCh2YWx1ZSwgZGlnaXRzID0gMyksIGNvbG9yPWNvbG9yKSwgIHNpemUgPSA1KSsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Yygid2hpdGUiLCJibGFjayIpLCBndWlkZT0nbm9uZScpCgpnZ3NhdmUoIi4uL091dHB1dC9CYXlFbnYvQ29ycmVsYXRpb25fcGxvdF8xODcwNXNucHMucG5nIiwgd2lkdGggPSA2LCBoZWlnaHQgPSA0LjUsIGRwaT0zMDApICAgIApgYGAKIVtdKC4uL091dHB1dC9CYXlFbnYvQ29ycmVsYXRpb25fcGxvdF8xODcwNXNucHMucG5nKXt3aWR0aD02MCV9CiogMThLIFNucHMgcHJvZHVjZWQgd2VpcmRlciByZXN1bHRzIHRoYW4gOEsgc25wcwoKCiMgUnVuIEJheUVudjIgbWF0cml4IGNvdmFyaWFuY2UgZXN0aW1hdGlvbnMgMTAgdGltZXMgdXNpbmcgODYyMSBTTlBzIGFuZCBhdmVyYWdlIHRoZSByZXN1bHRzIAoKYGBge3IgZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCiMgUnVuIEJheUVudjIgZm9yIGFsbCBTTlBTIHdpdGggZW52aXJvbm1lbnRhbCBkYXRhCmZvciAoaSBpbiAxOjEwKXsKICAgIHNpbmsocGFzdGUwKCIuLi9EYXRhL1NsdXJtc2NyaXB0cy9iYXllbnZfcnVuX3JlcCIsaSwiLnNoIikpCiAgICBjYXQoIiMhL2Jpbi9iYXNoIC1sXG4iKQogICAgY2F0KHBhc3RlMCgiI1NCQVRDSCAtLWpvYi1uYW1lPWJheWVudiIsaSwiIFxuIikpCiAgICBjYXQocGFzdGUwKCIjU0JBVENIIC0tbWVtPTE2RyBcbiIpKSAKICAgIGNhdChwYXN0ZTAoIiNTQkFUQ0ggLS1udGFza3M9OCBcbiIpKSAKICAgIGNhdChwYXN0ZTAoIiNTQkFUQ0ggLWUgYmF5ZW52IixpLCIuZXJyICBcbiIpKQogICAgY2F0KHBhc3RlMCgiI1NCQVRDSCAtLXRpbWU9MTc6MDA6MDAgIFxuIikpCiAgICBjYXQocGFzdGUwKCIjU0JBVENIIC1wIGhpZ2ggIFxuIikpCiAgICBjYXQoIlxuXG4iKQogICAgY2F0KCJtb2R1bGUgbG9hZCBiYXllbnYyIFxuXG4iKSAKICAgIAogICAgY2F0KHBhc3RlMCgiYmF5ZW52MiAtaSBZMjAxN19maWx0ZXJlZF9zbnBzZmlsZV84NjIxLnR4dCAtcCA2IC1rIDEwMDAwMCAtciAiLGksaSwiMjM0IiwiICA+IFkyMDE3X21hdHJpeF9yZXAiLGksIi5vdXQgXG4iKSkKICAgIHNpbmsoTlVMTCkKfQoKI3Bvb2xlZCB2ZXJzaW9uCmZvciAoaSBpbiAxOjEwKXsKICAgIHNpbmsocGFzdGUwKCIuLi9EYXRhL1NsdXJtc2NyaXB0cy9iYXllbnZfcnVuX3Bvb2xfcmVwIixpLCIuc2giKSkKICAgIGNhdCgiIyEvYmluL2Jhc2ggLWxcbiIpCiAgICBjYXQocGFzdGUwKCIjU0JBVENIIC0tam9iLW5hbWU9YmVQb29sIixpLCIgXG4iKSkKICAgIGNhdChwYXN0ZTAoIiNTQkFUQ0ggLS1tZW09MTZHIFxuIikpIAogICAgY2F0KHBhc3RlMCgiI1NCQVRDSCAtLW50YXNrcz04IFxuIikpIAogICAgY2F0KHBhc3RlMCgiI1NCQVRDSCAtZSBiZVBvb2wiLGksIi5lcnIgIFxuIikpCiAgICBjYXQocGFzdGUwKCIjU0JBVENIIC0tdGltZT03MjowMDowMCAgXG4iKSkKICAgIGNhdChwYXN0ZTAoIiNTQkFUQ0ggLXAgaGlnaCAgXG4iKSkKICAgIGNhdCgiXG5cbiIpCiAgICBjYXQoIm1vZHVsZSBsb2FkIGJheWVudjIgXG5cbiIpIAogICAgCiAgICBjYXQocGFzdGUwKCJiYXllbnYyIC1pIFkyMDE3X2ZpbHRlcmVkX3NucHNmaWxlXzg2MjEudHh0IC1zIHNhbXBsZXNpemUudHh0IC14IC1wIDYgLWsgMjAwMDAwIC1yICIsaSwiMjM0IixpLCIgID4gWTIwMTdfbWF0cml4X3Bvb2xfcmVwIixpLCIub3V0IFxuIikpCiAgICBzaW5rKE5VTEwpCn0KCgoKCgpgYGAKCgojIE9idGFpbiB0aGUgbWVhbiBjb3YgbWF0cml4IGZyb20gMTAgcmVwbGljYXRlcyAobm9uLXBvb2xlZCBtZXRob2QpICAKKiBOb24tcG9vbGVkIG1ldGhvZCBjcmVhdGVzIG11Y2ggbGFyZ2VyIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50cyAgIAoKYGBge3IgZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCiMgRmlyc3QgZXh0cmFjdCB0aGUgbGFzdCBjb3YgbWF0cml4CnNpbmsoIi4uL091dHB1dC9CYXlFbnYvZXh0cmFjdF9sYXN0X21hdHJpeC5zaCIpCmZvciAoaSBpbiAxOiAxMCl7CiAgICBjYXQocGFzdGUwKCJhd2sgJyQwID09IFwiVkFSLUNPVkFSIE1BVFJJWDogSVRFUiA9IDEwMDAwMFwiIHtpPTE7bmV4dH07aSAmJiBpKysgPD0gNicgWTIwMTdfbWF0cml4X3JlcCIsaSwiLm91dCA+IFkyMDE3X21hdHJpeF9yZXAiLGksIi50eHQgXG4iKSkKfQpzaW5rKE5VTEwpCgojYXZlcmFnZSB0aGUgMTAgY292YXJpYW5jZSBtYXRyaWNlcwpSZXA8LWxpc3QoKQpmb3IgKGkgaW4gMToxMCl7CiAgICBSZXBbW2ldXTwtYXMubWF0cml4KHJlYWQudGFibGUocGFzdGUwKCIuLi9PdXRwdXQvQmF5RW52L1kyMDE3X21hdHJpeF9yZXAiLGksIi50eHQiKSxoZWFkZXI9RikpCiAgICAKfQptZWFuX21hdCA9IGFwcGx5KHNpbXBsaWZ5MmFycmF5KFJlcCksIGMoMSwyKSwgbWVhbikKd3JpdGUudGFibGUobWVhbl9tYXQsICIuLi9PdXRwdXQvQmF5RW52L1kyMDE3X21lYW5fbWF0cml4LnR4dCIsIHF1b3RlID0gRiwgcm93Lm5hbWVzID0gRiwgY29sLm5hbWVzID0gRiwgc2VwPSJcdCIpCgptZWFuX21hdDwtYXMubWF0cml4KHJlYWQudGFibGUoIi4uL091dHB1dC9CYXlFbnYvWTIwMTdfbWVhbl9tYXRyaXgudHh0IiwgaGVhZGVyID0gRikpCm1hdCA9IGNvdjJjb3IobWVhbl9tYXQpCnJvdy5uYW1lcyhtYXQpPC1wb3BzMgpjb2xuYW1lcyhtYXQpPC1wb3BzMgptYXQ8LW1hdFssYygiVEIxNyIsIlBXUzE3IiwiU1MxNyIsIkJDMTciLCJXQTE3IiwiQ0ExNyIpXQptYXQ8LW1hdFtjKCJUQjE3IiwiUFdTMTciLCJTUzE3IiwiQkMxNyIsIldBMTciLCJDQTE3IiksXQoKe3BkZigiLi4vT3V0cHV0L0JheUVudi9Db3JwbG90X21lYW4xMF95MjAxN184NjIxc25wcy5wZGYiLCB3aWR0aCA9IDUsIGhlaWdodD01KQpjb3JycGxvdChtYXQsIGlzLmNvcnI9VCkKZGV2Lm9mZigpfQoKbWF0MjwtbWF0CmRpYWcobWF0Mik8LU5BCm1hdDJbdXBwZXIudHJpKG1hdDIpXTwtTkEKbWF0Mm08LW1lbHQobWF0MikKCm1hdDJtJFZhcjE8LWZhY3RvcihtYXQybSRWYXIxLCBsZXZlbHM9YygiVEIxNyIsIlBXUzE3IiwiU1MxNyIsIkJDMTciLCJXQTE3IiwiQ0ExNyIpKQptYXQybSRWYXIyPC1mYWN0b3IobWF0Mm0kVmFyMiwgbGV2ZWxzPWMoIlRCMTciLCJQV1MxNyIsIlNTMTciLCJCQzE3IiwiV0ExNyIsIkNBMTciKSkKCm1hdDJtJGNvbG9yPC0iYSIKbWF0Mm0kY29sb3JbbWF0Mm0kdmFsdWU+MC4wMDhdPC0iYiIKZ2dwbG90KG1hdDJtLCBhZXMoeD1WYXIxLCB5PVZhcjIsIGZpbGw9dmFsdWUpKSsKICAgIGdlb21fdGlsZShjb2xvciA9ICJ3aGl0ZSIpKwogICAgIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKGNvbG9ycz1jKCIjMDczNDY2Iiwid2hpdGUiKSwgbGltaXRzPWMobWluKG1hdDJtJHZhbHVlLCBuYS5ybT1UKSwgbWF4KG1hdDJtJHZhbHVlLCBuYS5ybT1UKSswLjAxKSwgbmEudmFsdWU9ImdyYXk5MCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgbmFtZT0iQ29ycmVsYXRpb24iKSsKICAgIHRoZW1lX21pbmltYWwoKSsgeGxhYigiIikreWxhYigiIikrCiAgICBnZW9tX3RleHQoYWVzKFZhcjEsIFZhcjIsIGxhYmVsID0gcm91bmQodmFsdWUsIGRpZ2l0cyA9IDMpLCBjb2xvcj1jb2xvciksICBzaXplID0gNSkrCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoIndoaXRlIiwiYmxhY2siKSwgZ3VpZGU9J25vbmUnKQoKZ2dzYXZlKCIuLi9PdXRwdXQvQmF5RW52L0NvcnJlbGF0aW9uX21lYW5fcGxvdF84NjIxc25wcy5wbmciLCB3aWR0aCA9IDYsIGhlaWdodCA9IDQuNSwgZHBpPTMwMCkgICAgCgpgYGAKIVtdKC4uL091dHB1dC9CYXlFbnYvQ29ycGxvdF9tZWFuMTBfeTIwMTdfODYyMXNucHMucG5nKXt3aWR0aD02MCV9CgoKIVtdKC4uL091dHB1dC9CYXlFbnYvQ29ycmVsYXRpb25fbWVhbl9wbG90Xzg2MjFzbnBzLnBuZyl7d2lkdGg9NjAlfQoKIyMgQ292YXJpYW5jZSBtYXRyaWNpZXMgZnJvbSB0aGUgcG9vbGVkIE5HUyBzZXR0aW5nCgpgYGB7ciBldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpQb29sPC1saXN0KCkKZm9yIChpIGluIDE6Mil7CiAgICBQb29sW1tpXV08LWFzLm1hdHJpeChyZWFkLnRhYmxlKHBhc3RlMCgiLi4vT3V0cHV0L0JheUVudi9ZMjAxN19tYXRyaXhfcG9vbF9yZXAiLGksIi50eHQiKSxoZWFkZXI9RikpCiAgICAKfQptZWFuX21hdCA9IGFwcGx5KHNpbXBsaWZ5MmFycmF5KFBvb2wpLCBjKDEsMiksIG1lYW4pCiN3cml0ZS50YWJsZShtZWFuX21hdCwgIi4uL091dHB1dC9CYXlFbnYvWTIwMTdfbWVhbl9wb29sX21hdHJpeC50eHQiLCBxdW90ZSA9IEYsIHJvdy5uYW1lcyA9IEYsIGNvbC5uYW1lcyA9IEYsIHNlcD0iXHQiKQoKbWF0ID0gY292MmNvcihtZWFuX21hdCkKcm93Lm5hbWVzKG1hdCk8LXBvcHMyCmNvbG5hbWVzKG1hdCk8LXBvcHMyCm1hdDwtbWF0WyxjKCJUQjE3IiwiUFdTMTciLCJTUzE3IiwiQkMxNyIsIldBMTciLCJDQTE3IildCm1hdDwtbWF0W2MoIlRCMTciLCJQV1MxNyIsIlNTMTciLCJCQzE3IiwiV0ExNyIsIkNBMTciKSxdCgptYXQyPC1tYXQKZGlhZyhtYXQyKTwtTkEKbWF0Mlt1cHBlci50cmkobWF0MildPC1OQQptYXQybTwtbWVsdChtYXQyKQptYXQybSRWYXIxPC1mYWN0b3IobWF0Mm0kVmFyMSwgbGV2ZWxzPWMoIlRCMTciLCJQV1MxNyIsIlNTMTciLCJCQzE3IiwiV0ExNyIsIkNBMTciKSkKbWF0Mm0kVmFyMjwtZmFjdG9yKG1hdDJtJFZhcjIsIGxldmVscz1jKCJUQjE3IiwiUFdTMTciLCJTUzE3IiwiQkMxNyIsIldBMTciLCJDQTE3IikpCm1hdDJtJGNvbG9yPC0iYSIKbWF0Mm0kY29sb3JbbWF0Mm0kdmFsdWU+MC4wMV08LSJiIgpnZ3Bsb3QobWF0Mm0sIGFlcyh4PVZhcjEsIHk9VmFyMiwgZmlsbD12YWx1ZSkpKwogICAgZ2VvbV90aWxlKGNvbG9yID0gIndoaXRlIikrCiAgICAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3JzPWMoIiMwNzM0NjYiLCJ3aGl0ZSIpLCBsaW1pdHM9YyhtaW4obWF0Mm0kdmFsdWUsIG5hLnJtPVQpLCBtYXgobWF0Mm0kdmFsdWUsIG5hLnJtPVQpKzAuMDEpLCBuYS52YWx1ZT0iZ3JheTkwIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lPSJDb3JyZWxhdGlvbiIpKwogICAgdGhlbWVfbWluaW1hbCgpKyB4bGFiKCIiKSt5bGFiKCIiKSsKICAgIGdlb21fdGV4dChhZXMoVmFyMSwgVmFyMiwgbGFiZWwgPSByb3VuZCh2YWx1ZSwgZGlnaXRzID0gMyksIGNvbG9yPWNvbG9yKSwgIHNpemUgPSA0KSsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Yygid2hpdGUiLCJibGFjayIpLCBndWlkZT0nbm9uZScpCgpnZ3NhdmUoIi4uL091dHB1dC9CYXlFbnYvQ29ycmVsYXRpb25fbWVhbl9wb29sZWRfcGxvdF84NjIxc25wcy5wbmciLCB3aWR0aCA9IDYsIGhlaWdodCA9IDQuNSwgZHBpPTMwMCkgICAKCmBgYAohW10oLi4vT3V0cHV0L0JheUVudi9Db3JyZWxhdGlvbl9tZWFuX3Bvb2xlZF9wbG90Xzg2MjFzbnBzLnBuZyl7d2lkdGg9NjAlfQoKCiMjIFJ1biBCYXlFbnYyIHdpdGggdGhlIGVudmlyb25tZW50YWwgdmFyaWFibGUgZmlsZQoqIGJheWVudl9ydW5fZW52Mi5zaCAoVXNpbmcgdGhlIG1lYW4gbWF0cml4IGZyb20gOSByZXBsaWNhdGVzIChub24tcG9vbCkpCgpgYGB7ciBldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKYmY8LXJlYWQudGFibGUoIi4uL091dHB1dC9CYXlFbnYvTWVhbjkuYmYiLCBoZWFkZXIgPSBGKQpjb2xuYW1lcyhiZik8LWMoImJhdGNoX2lkIiwgIkJGIiwicmhvIiwiciIpCgojIExpc3QgYWxsIHNwbGl0IGZpbGUgbmFtZXM6CiMgbHMgc25wX2JhdGNoKiA+c25wX2ZpbGVuYW1lcy50eHQKCm5hbWVzPC1yZWFkLnRhYmxlKCIuLi9PdXRwdXQvQmF5RW52L3NucF9maWxlbmFtZXMudHh0IikKCiNzbnAgbmFtZXMKc25wczwtcmVhZC5jc3YoIi4uL091dHB1dC9CYXlFbnYvWTIwMTdfZmlsdGVyZWRfc25wc2ZpbGVfODYyMS5jc3YiKQpzbnBzX25hbWU8LXNucHNbc2VxKDEsbnJvdyhzbnBzKSxieT0yKSxjKCJjaHIiLCJwb3MiKV0Kc25wc19uYW1lJGJhdGNoX2lkPC1uYW1lcyRWMQoKIyBub3cgbWVyZ2UgdGhlIHR3byB0YWJsZXMKYmY8LW1lcmdlKHNucHNfbmFtZSwgYmYsIGJ5PSJiYXRjaF9pZCIpCmJmJGNocm9tPC1hcy5pbnRlZ2VyKGdzdWIoImNociIsJycsYmYkY2hyKSkKYmY8LWJmW29yZGVyKGJmJGNocm9tLCBiZiRwb3MpLF0KYmYkbjwtMTpucm93KGJmKQpldmVuczwtcGFzdGUwKCJjaHIiLHNlcSgyLDI2LCBieT0yKSkKYmYkY29sb3I8LSJhIgpiZiRjb2xvcltiZiRjaHIgJWluJSBldmVuc108LSJiIgpnZ3Bsb3QoYmYsIGFlcyh4PW4seT1CRiwgY29sb3I9Y29sb3IpKSt4bGFiKCcnKSsKICAgIGdlb21fcG9pbnQoc2l6ZT0wLjUsIGFscGhhPTAuNikreWxhYigiQmF5ZXMgZmFjYXRvciIpKwogICAgdGhlbWVfY2xhc3NpYygpKwogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJncmF5NzAiLCJzdGVlbGJsdWUiKSwgZ3VpZGU9Im5vbmUiKSsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKQpnZ3NhdmUoIi4uL091dHB1dC9iYXlzRmFjdG9yX2Fjcm9zc0dlbm9tZV9ZMjAxN19ydW4xLnBuZyIsIHdpZHRoID0gNywgaGVpZ2h0ID0gMywgZHBpPTMwMCkKCgojcmVtb3ZlIHRoZSAyIG91dGxpZXJzICYgbG9nIHNjYWxlCmdncGxvdChiZiwgYWVzKHg9bix5PUJGLCBjb2xvcj1jb2xvcikpK3hsYWIoJycpKwogICAgZ2VvbV9wb2ludChzaXplPTAuNSwgYWxwaGE9MC42KSt5bGFiKCJCYXllcyBmYWNhdG9yIikrCiAgICB0aGVtZV9jbGFzc2ljKCkrCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoImdyYXk3MCIsInN0ZWVsYmx1ZSIpLCBndWlkZT0ibm9uZSIpKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCkpKwogICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGxhYmVsX2NvbW1hKCksIHRyYW5zID0gJ2xvZzEwJywgbGltaXRzPWMoMC4wNSwxMDAwKSkKZ2dzYXZlKCIuLi9PdXRwdXQvYmF5c0ZhY3Rvcl9hY3Jvc3NHZW5vbWVfWTIwMTdfcnVuMV9sb2dzY2FsZWQucG5nIiwgd2lkdGggPSA3LCBoZWlnaHQgPSAzLCBkcGk9MzAwKQoKI3JobyB2YWx1ZXMKZ2dwbG90KGJmLCBhZXMoeD1uLHk9cmhvLCBjb2xvcj1jb2xvcikpK3hsYWIoJycpKwogICAgZ2VvbV9wb2ludChzaXplPTAuNSwgYWxwaGE9MC42KSt5bGFiKCJTcGVhZXJtYW4ncyByaG8iKSsKICAgIHRoZW1lX2NsYXNzaWMoKSsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiZ3JheTcwIiwic3RlZWxibHVlIiksIGd1aWRlPSJub25lIikrCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcy54PWVsZW1lbnRfYmxhbmsoKSkKZ2dzYXZlKCIuLi9PdXRwdXQvUmhvX2Fjcm9zc0dlbm9tZV9ZMjAxN19ydW4xLnBuZyIsIHdpZHRoID0gNywgaGVpZ2h0ID0gMywgZHBpPTMwMCkKCmBgYAoKCgoKCmBgYHtyIGV2YWw9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmBgYAoKYGBge3IgZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KYGBgCgpgYGB7ciBldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpgYGAKCgoKCmBgYHtyIGV2YWw9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnNucDIwMTc8LWZ1bmN0aW9uKGRmLCB4KXsKICAgIHMxNzwtZGZbLGMoMToyLGdyZXAoIjE3Iixjb2xuYW1lcyhkZikpKV0KICAgIHJlbW92ZTwtd2hpY2gocm93U3VtcyhzMTdbLDM6OF0pPD0zKQogICAgI2NoZWNrIGlmIGV2ZW4gb3Igb2RkIG51bWJlcnMKICAgIGV2ZW48LXJlbW92ZVtzYXBwbHkocmVtb3ZlLCBmdW5jdGlvbih4KSB4JSUyPT0wKV0KICAgIHJlbW92ZV9vZGQ8LXJlbW92ZVshKHJlbW92ZSAlaW4lIGV2ZW4pXQogICAgcmVtPC1jKHJlbW92ZV9vZGQsIHJlbW92ZV9vZGQrMSwgZXZlbiwgZXZlbi0xKQogICAgcmVtPC1yZW1bb3JkZXIocmVtKV0KICAgIHMxN25ldzwtczE3Wy1yZW0sXQogICAgcmV0dXJuKHMxN25ldykKfQoKc25wczIwMTdfMjU8LXNucDIwMTcoc25wczIpCnRhYmxlKHNucHMyMDE3XzI1JGNocikKCmBgYAoKCgo=